From e847d6091044c6d972faf448e1bf2be178e5d77f Mon Sep 17 00:00:00 2001 From: xvanc Date: Wed, 13 Sep 2023 09:35:30 -0500 Subject: [PATCH] riscv: implement feature dectection --- common/entry.s3.c | 13 +++ common/sys/cpu.h | 24 ++++ common/sys/cpu_riscv.c | 255 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 292 insertions(+) create mode 100644 common/sys/cpu_riscv.c diff --git a/common/entry.s3.c b/common/entry.s3.c index da91b4cc..56559a28 100644 --- a/common/entry.s3.c +++ b/common/entry.s3.c @@ -22,6 +22,7 @@ #include #include #include +#include void stage3_common(void); @@ -130,6 +131,18 @@ noreturn void stage3_common(void) { init_io_apics(); #endif +#if defined (__riscv) +#if defined (UEFI) + RISCV_EFI_BOOT_PROTOCOL *rv_proto = get_riscv_boot_protocol(); + if (rv_proto == NULL || rv_proto->GetBootHartId(rv_proto, &bsp_hartid) != EFI_SUCCESS) { + panic(false, "failed to get BSP's hartid"); + } +#else +#error riscv: only UEFI is supported +#endif + init_riscv(); +#endif + term_notready(); menu(true); diff --git a/common/sys/cpu.h b/common/sys/cpu.h index 12d48137..bd8609dd 100644 --- a/common/sys/cpu.h +++ b/common/sys/cpu.h @@ -320,6 +320,30 @@ inline uint64_t rdtsc(void) { locked_read__ret; \ }) +extern size_t bsp_hartid; + +struct riscv_hart { + struct riscv_hart *next; + const char *isa_string; + size_t hartid; + uint32_t acpi_uid; + uint8_t mmu_type; + uint8_t flags; +}; + +#define RISCV_HART_COPROC ((uint8_t)1 << 0) // is a coprocessor +#define RISCV_HART_HAS_MMU ((uint8_t)1 << 1) // `mmu_type` field is valid + +extern struct riscv_hart *hart_list; + +bool riscv_check_isa_extension_for(size_t hartid, const char *ext, size_t *maj, size_t *min); + +static inline bool riscv_check_isa_extension(const char *ext, size_t *maj, size_t *min) { + return riscv_check_isa_extension_for(bsp_hartid, ext, maj, min); +} + +void init_riscv(void); + #else #error Unknown architecture #endif diff --git a/common/sys/cpu_riscv.c b/common/sys/cpu_riscv.c new file mode 100644 index 00000000..37dcff90 --- /dev/null +++ b/common/sys/cpu_riscv.c @@ -0,0 +1,255 @@ + +#if defined(__riscv) + +#include +#include +#include +#include +#include +#include + +// ACPI RISC-V Hart Capabilities Table +struct rhct { + struct sdt header; + uint32_t flags; + uint64_t time_base_frequency; + uint32_t nodes_len; + uint32_t nodes_offset; + uint8_t nodes[]; +} __attribute__((packed)); + +#define RHCT_ISA_STRING 0 +#define RHCT_CMO 1 +#define RHCT_MMU 2 +#define RHCT_HART_INFO 65535 + +struct rhct_header { + uint16_t type; // node type + uint16_t size; // node size (bytes) + uint16_t revision; // node revision +} __attribute__((packed)); + +// One `struct rhct_hart_info` structure exists per hart in the system. +// The `offsets` array points to other entries in the RHCT associated with the +// hart. +struct rhct_hart_info { + struct rhct_header header; + uint16_t offsets_len; + uint32_t acpi_processor_uid; + uint32_t offsets[]; +} __attribute__((packed)); + +struct rhct_isa_string { + struct rhct_header header; + uint16_t isa_string_len; + const char isa_string[]; +} __attribute__((packed)); + +#define RISCV_MMU_TYPE_SV39 0 +#define RISCV_MMU_TYPE_SV48 1 +#define RISCV_MMU_TYPE_SV57 2 + +struct rhct_mmu { + struct rhct_header header; + uint8_t reserved0; + uint8_t mmu_type; +} __attribute__((packed)); + +size_t bsp_hartid; +struct riscv_hart *hart_list; +static struct riscv_hart *bsp_hart; + +static struct riscv_hart *riscv_get_hart(size_t hartid) { + for (struct riscv_hart *hart = hart_list; hart != NULL; hart = hart->next) { + if (hart->hartid == hartid) { + return hart; + } + } + panic(false, "no `struct riscv_hart` for hartid %u", hartid); +} + +static inline struct rhct_hart_info *rhct_get_hart_info(struct rhct *rhct, uint32_t acpi_uid) { + uint32_t offset = rhct->nodes_offset; + for (uint32_t i = 0; i < rhct->nodes_len; i++) { + struct rhct_hart_info *node = (void *)((uintptr_t)rhct + offset); + if (node->header.type == RHCT_HART_INFO && node->acpi_processor_uid == acpi_uid) { + return node; + } + offset += node->header.size; + } + return NULL; +} + +void riscv_init(void) { + struct madt *madt = acpi_get_table("APIC", 0); + struct rhct *rhct = acpi_get_table("RHCT", 0); + if (madt == NULL || rhct == NULL) { + panic(false, "riscv: requires acpi"); + } + + for (uint8_t *madt_ptr = (uint8_t *)madt->madt_entries_begin; + (uintptr_t)madt_ptr < (uintptr_t)madt + madt->header.length; madt_ptr += *(madt_ptr + 1)) { + if (*madt_ptr != 0x18) { + continue; + } + struct madt_riscv_intc *intc = (struct madt_riscv_intc *)madt_ptr; + + // Ignore harts we can't do anything with. + if (!(intc->flags & MADT_RISCV_INTC_ENABLED || + intc->flags & MADT_RISCV_INTC_ONLINE_CAPABLE)) { + continue; + } + + uint32_t acpi_uid = intc->acpi_processor_uid; + size_t hartid = intc->hartid; + + struct rhct_hart_info *hart_info = rhct_get_hart_info(rhct, acpi_uid); + if (hart_info == NULL) { + panic(false, "riscv: missing rhct node for hartid %u", hartid); + } + + const char *isa_string = NULL; + uint8_t mmu_type = 0; + uint8_t flags = 0; + + for (uint32_t i = 0; i < hart_info->offsets_len; i++) { + const struct rhct_header *node = (void *)((uintptr_t)rhct + hart_info->offsets[i]); + switch (node->type) { + case RHCT_ISA_STRING: + isa_string = ((struct rhct_isa_string *)node)->isa_string; + break; + case RHCT_MMU: + mmu_type = ((struct rhct_mmu *)node)->mmu_type; + flags |= RISCV_HART_HAS_MMU; + break; + } + } + + if (isa_string == NULL) { + print("riscv: missing isa string for hartid %u, skipping.\n", hartid); + continue; + } + + struct riscv_hart *hart = ext_mem_alloc(sizeof(struct riscv_hart)); + if (hart == NULL) { + panic(false, "out of memory"); + } + + hart->hartid = hartid; + hart->acpi_uid = acpi_uid; + hart->isa_string = isa_string; + hart->mmu_type = mmu_type; + hart->flags = flags; + + hart->next = hart_list; + hart_list = hart; + + if (hart->hartid == bsp_hartid) { + bsp_hart = hart; + } + } + + if (bsp_hart == NULL) { + panic(false, "riscv: missing `struct riscv_hart` for BSP"); + } + + if (strncasecmp(bsp_hart->isa_string, "rv64i", 5)) { + panic(false, "unsupported cpu: %s", bsp_hart->isa_string); + } + + for (struct riscv_hart *hart = hart_list; hart != NULL; hart = hart->next) { + if (hart != bsp_hart && strcmp(bsp_hart->isa_string, hart->isa_string)) { + hart->flags |= RISCV_HART_COPROC; + } + } +} + +struct isa_extension { + const char *name; + size_t name_len; + uint32_t ver_maj; + uint32_t ver_min; +}; + +// Parse the next sequence of digit characters into an integer. +static bool parse_number(const char **s, size_t *_n) { + size_t n = 0; + bool parsed = false; + while (isdigit(**s)) { + n *= 10; + n += *(*s)++ - '0'; + parsed = true; + } + *_n = n; + return parsed; +} + +// Parse the next extension from an ISA string. +static bool parse_extension(const char **s, struct isa_extension *ext) { + if (**s == '\0') { + return false; + } + + const char *name = *s; + size_t name_len = 1; + if (**s == 's' || **s == 'S' || **s == 'x' || **s == 'X' || **s == 'z' || **s == 'Z') { + while (isalpha((*s)[name_len])) { + name_len++; + } + } + *s += name_len; + + size_t maj = 0, min = 0; + if (parse_number(s, &maj)) { + if (**s == 'p') { + *s += 1; + parse_number(s, &min); + } + } + + while (**s == '_') { + *s += 1; + } + + if (ext) { + ext->name = name; + ext->name_len = name_len; + ext->ver_maj = maj; + ext->ver_min = min; + } + return true; +} + +static bool extension_matches(const struct isa_extension *ext, const char *name) { + for (size_t i = ext->name_len; i > 0; i--) { + const char c1 = tolower(ext->name[i]); + const char c2 = tolower(*name++); + if (c2 == '\0' || c1 != c2) { + return false; + } + } + // Make sure `name` is not longer. + return *name == '\0'; +} + +bool riscv_check_isa_extension_for(size_t hartid, const char *name, size_t *maj, size_t *min) { + const char *isa_string = riscv_get_hart(hartid)->isa_string; + + struct isa_extension ext; + while (parse_extension(&isa_string, &ext)) { + if (!extension_matches(&ext, name)) { + continue; + } + if (maj) { + *maj = ext.ver_maj; + } + if (min) { + *min = ext.ver_min; + } + return true; + } + + return false; +} + +#endif \ No newline at end of file