:: commit e847d6091044c6d972faf448e1bf2be178e5d77f

xvanc <xvancm@gmail.com> — 2023-09-13 14:35

parents: 3d67b22786

riscv: implement feature dectection

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 <drivers/disk.h>
 #include <sys/lapic.h>
 #include <lib/readline.h>
+#include <sys/cpu.h>
 
 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 <lib/acpi.h>
+#include <lib/misc.h>
+#include <lib/print.h>
+#include <mm/pmm.h>
+#include <stddef.h>
+#include <stdint.h>
+
+// 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
tab: 248 wrap: offon