:: commit 7767f246899643943dc630c54f1278a9a411cb44

Yao Zi <ziyao@disroot.org> — 2024-06-08 10:19

parents: 3a44b45029

protos/linux_riscv64: support linux protocol on riscv64

diff --git a/common/menu.c b/common/menu.c
index c54f4a0c..fac91d85 100644
--- a/common/menu.c
+++ b/common/menu.c
@@ -1104,11 +1104,11 @@ noreturn void boot(char *config) {
     if (!strcmp(proto, "limine")) {
         limine_load(config, cmdline);
     } else if (!strcmp(proto, "linux")) {
-#if defined (__x86_64__) || defined (__i386__)
+#if defined (__x86_64__) || defined (__i386__) || defined (__riscv64)
         linux_load(config, cmdline);
 #else
         quiet = false;
-        print("TODO: Linux is not available on aarch64 or riscv64.\n\n");
+        print("TODO: Linux is not available on aarch64.\n\n");
 #endif
     } else if (!strcmp(proto, "multiboot1") || !strcmp(proto, "multiboot")) {
 #if defined (__x86_64__) || defined (__i386__)
diff --git a/common/protos/linux_riscv64.c b/common/protos/linux_riscv64.c
new file mode 100644
index 00000000..899c2147
--- /dev/null
+++ b/common/protos/linux_riscv64.c
@@ -0,0 +1,125 @@
+#if defined(__riscv64)
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdnoreturn.h>
+#include <protos/linux.h>
+#include <fs/file.h>
+#include <lib/libc.h>
+#include <lib/misc.h>
+#include <lib/term.h>
+#include <lib/config.h>
+#include <lib/print.h>
+#include <lib/uri.h>
+#include <mm/pmm.h>
+#include <sys/idt.h>
+#include <lib/fb.h>
+#include <lib/acpi.h>
+#include <lib/fdt.h>
+#include <libfdt/libfdt.h>
+
+// The following definitions and struct were copied and adapted from Linux
+// kernel headers released under GPL-2.0 WITH Linux-syscall-note
+// allowing their inclusion in non GPL compliant code.
+
+struct linux_header {
+    uint32_t code0;
+    uint32_t code1;
+    uint64_t text_offset;
+    uint64_t image_size;
+    uint64_t flags;
+    uint32_t version;
+    uint32_t res1;
+    uint64_t res2;
+    uint64_t res3;          // originally 'magic' field, deprecated
+    uint32_t magic2;
+    uint32_t res4;
+} __attribute__((packed));
+
+// End of Linux code
+
+#define LINUX_HEADER_MAGIC2             0x05435352
+#define LINUX_HEADER_MAJOR_VER(ver)     (((ver) >> 16) & 0xffff)
+#define LINUX_HEADER_MINOR_VER(ver)     (((ver) >> 0)  & 0xffff)
+
+noreturn void linux_load(char *config, char *cmdline) {
+    struct file_handle *kernel_file;
+
+    char *kernel_path = config_get_value(config, 0, "KERNEL_PATH");
+    if (kernel_path == NULL) {
+        panic(true, "linux: KERNEL_PATH not specified");
+    }
+
+    if ((kernel_file = uri_open(kernel_path)) == NULL) {
+        panic(true, "linux: failed to open kernel `%s`. Is the path correct?", kernel_path);
+    }
+
+    struct linux_header header;
+    fread(kernel_file, &header, 0, sizeof(header));
+
+    if (header.magic2 != LINUX_HEADER_MAGIC2) {
+        panic(true, "linux: kernel header magic does not match");
+    }
+
+    printv("linux: boot protocol version %d.%d\n",
+           LINUX_HEADER_MAJOR_VER(header.version),
+           LINUX_HEADER_MINOR_VER(header.version));
+    if (LINUX_HEADER_MINOR_VER(header.version) < 2) {
+        panic(true, "linux: protocols < 0.2 are not supported");
+    }
+
+    size_t kernel_size = kernel_file->size;
+    void *kernel_base = ext_mem_alloc_type_aligned(
+                ALIGN_UP(kernel_size, 4096),
+                MEMMAP_KERNEL_AND_MODULES, 2 * 1024 * 1024);
+    fread(kernel_file, kernel_base, 0, kernel_size);
+    fclose(kernel_file);
+    printv("linux: loaded kernel `%s` at %x, size %u\n", kernel_path, kernel_base, kernel_size);
+
+    void *dtb = get_device_tree_blob();
+    if (!dtb) {
+        panic(true, "linux: no device tree blob found");
+    }
+
+    int ret = fdt_set_chosen_string(dtb, "bootargs", cmdline);
+    if (ret < 0) {
+       printv("linux: cannot set bootargs: `%s`\n", fdt_strerror(ret));
+    }
+
+    char *module_path = config_get_value(config, 0, "MODULE_PATH");
+    if (module_path) {
+        struct file_handle *module_file = uri_open(module_path);
+        if (!module_file) {
+            panic(true, "linux: failed to open module `%s`. Is the path correct?", module_path);
+        }
+
+        size_t module_size = module_file->size;
+        void *module_base = ext_mem_alloc_type_aligned(
+                        ALIGN_UP(module_size, 4096),
+                        MEMMAP_KERNEL_AND_MODULES, 4096);
+
+        fread(module_file, module_base, 0, module_size);
+        fclose(module_file);
+        printv("linux: loaded module `%s` at %x, size %u\n", module_path, module_base, module_size);
+
+        ret = fdt_set_chosen_uint64(dtb, "linux,initrd-start", (uint64_t)module_base);
+        if (ret < 0) {
+            printv("linux: cannot set initrd parameter: %s\n", fdt_strerror(ret));
+        }
+
+        ret = fdt_set_chosen_uint64(dtb, "linux,initrd-end", (uint64_t)(module_base + module_size));
+        if (ret < 0) {
+            printv("linux: cannot set initrd parameter: %s\n", fdt_strerror(ret));
+        }
+    }
+
+    printv("linux: bsp hart %d, device tree blob at %x\n", bsp_hartid, dtb);
+
+    void (*kernel_entry)(uint64_t hartid, uint64_t dtb) = kernel_base;
+    asm ("csrci   sstatus, 0x2\n\t"
+         "csrw    sie, zero\n\t");
+    kernel_entry(bsp_hartid, (uint64_t)dtb);
+    __builtin_unreachable();
+}
+
+#endif
tab: 248 wrap: offon