diff --git a/common/lib/misc.c b/common/lib/misc.c index 614842bf..366d1f84 100644 --- a/common/lib/misc.c +++ b/common/lib/misc.c @@ -16,7 +16,7 @@ EFI_BOOT_SERVICES *gBS; EFI_RUNTIME_SERVICES *gRT; EFI_HANDLE efi_image_handle; EFI_MEMORY_DESCRIPTOR *efi_mmap = NULL; -UINTN efi_mmap_size = 0, efi_desc_size = 0; +UINTN efi_mmap_size = 0, efi_desc_size = 0, efi_mmap_key = 0; UINT32 efi_desc_ver = 0; #endif @@ -205,9 +205,8 @@ bool efi_exit_boot_services(void) { EFI_MEMORY_DESCRIPTOR tmp_mmap[1]; efi_mmap_size = sizeof(tmp_mmap); - UINTN mmap_key = 0; - gBS->GetMemoryMap(&efi_mmap_size, tmp_mmap, &mmap_key, &efi_desc_size, &efi_desc_ver); + gBS->GetMemoryMap(&efi_mmap_size, tmp_mmap, &efi_mmap_key, &efi_desc_size, &efi_desc_ver); efi_mmap_size += 4096; @@ -232,13 +231,13 @@ bool efi_exit_boot_services(void) { size_t retries = 0; retry: - status = gBS->GetMemoryMap(&efi_mmap_size, efi_mmap, &mmap_key, &efi_desc_size, &efi_desc_ver); + status = gBS->GetMemoryMap(&efi_mmap_size, efi_mmap, &efi_mmap_key, &efi_desc_size, &efi_desc_ver); if (retries == 0 && status) { goto fail; } // Be gone, UEFI! - status = gBS->ExitBootServices(efi_image_handle, mmap_key); + status = gBS->ExitBootServices(efi_image_handle, efi_mmap_key); if (status) { if (retries == 128) { goto fail; diff --git a/common/lib/misc.h b/common/lib/misc.h index 3995e59b..38810e32 100644 --- a/common/lib/misc.h +++ b/common/lib/misc.h @@ -21,7 +21,7 @@ extern EFI_BOOT_SERVICES *gBS; extern EFI_RUNTIME_SERVICES *gRT; extern EFI_HANDLE efi_image_handle; extern EFI_MEMORY_DESCRIPTOR *efi_mmap; -extern UINTN efi_mmap_size, efi_desc_size; +extern UINTN efi_mmap_size, efi_desc_size, efi_mmap_key; extern UINT32 efi_desc_ver; extern bool efi_boot_services_exited; diff --git a/common/menu.c b/common/menu.c index cce5a91d..18405626 100644 --- a/common/menu.c +++ b/common/menu.c @@ -1190,12 +1190,7 @@ noreturn void boot(char *config) { if (!strcmp(proto, "limine")) { limine_load(config, cmdline); } else if (!strcmp(proto, "linux")) { -#if defined (__loongarch64) - quiet = false; - print("TODO: Linux is not available on LoongArch64.\n\n"); -#else linux_load(config, cmdline); -#endif } else if (!strcmp(proto, "multiboot1") || !strcmp(proto, "multiboot")) { #if defined (__x86_64__) || defined (__i386__) multiboot1_load(config, cmdline); diff --git a/common/protos/linux_risc.c b/common/protos/linux_risc.c index 25fb8ceb..f7cd738d 100644 --- a/common/protos/linux_risc.c +++ b/common/protos/linux_risc.c @@ -1,4 +1,4 @@ -#if defined(__riscv) || defined(__aarch64__) +#if defined(__riscv) || defined(__aarch64__) || defined(__loongarch__) #include #include @@ -22,6 +22,7 @@ // kernel headers released under GPL-2.0 WITH Linux-syscall-note // allowing their inclusion in non GPL compliant code. +#if defined(__riscv) || defined(__aarch64__) struct linux_header { uint32_t code0; uint32_t code1; @@ -35,54 +36,87 @@ struct linux_header { uint32_t magic2; uint32_t res4; } __attribute__((packed)); +#elif defined(__loongarch__) +struct linux_header { + uint32_t mz; + uint32_t res0; + uint64_t kernel_entry; + uint64_t image_size; + uint64_t load_offset; + uint64_t res1; + uint64_t res2; + uint64_t res3; + uint32_t magic2; // LINUX_PE_MAGIC + uint32_t pe_offset; +} __attribute__((packed)); +#else +#error "Unknown architecture" +#endif struct linux_efi_memreserve { - int size; + int size; int count; uint64_t next; }; +struct linux_efi_boot_memmap { + UINTN map_size; + UINTN desc_size; + uint32_t desc_ver; + UINTN map_key; + UINTN buff_size; + EFI_MEMORY_DESCRIPTOR descs[]; +}; + +struct linux_efi_initrd { + UINTN base; + UINTN size; +}; + // End of Linux code +struct boot_param { + void *kernel_base; + size_t kernel_size; + void *module_base; + size_t module_size; + char *cmdline; + void *dtb; + struct linux_efi_boot_memmap *memmap; +}; + #if defined(__riscv) #define LINUX_HEADER_MAGIC2 0x05435352 #define LINUX_HEADER_MAJOR_VER(ver) (((ver) >> 16) & 0xffff) #define LINUX_HEADER_MINOR_VER(ver) (((ver) >> 0) & 0xffff) #elif defined(__aarch64__) #define LINUX_HEADER_MAGIC2 0x644d5241 +#elif defined(__loongarch__) +#define LINUX_HEADER_MAGIC2 0x818223cd #endif -void add_framebuffer(struct fb_info *fb) { - struct screen_info *screen_info = ext_mem_alloc(sizeof(struct screen_info)); - - screen_info->capabilities = VIDEO_CAPABILITY_64BIT_BASE | VIDEO_CAPABILITY_SKIP_QUIRKS; - screen_info->flags = VIDEO_FLAGS_NOCURSOR; - screen_info->lfb_base = (uint32_t)fb->framebuffer_addr; - screen_info->ext_lfb_base = (uint32_t)(fb->framebuffer_addr >> 32); - screen_info->lfb_size = fb->framebuffer_pitch * fb->framebuffer_height; - screen_info->lfb_width = fb->framebuffer_width; - screen_info->lfb_height = fb->framebuffer_height; - screen_info->lfb_depth = fb->framebuffer_bpp; - screen_info->lfb_linelength = fb->framebuffer_pitch; - screen_info->red_size = fb->red_mask_size; - screen_info->red_pos = fb->red_mask_shift; - screen_info->green_size = fb->green_mask_size; - screen_info->green_pos = fb->green_mask_shift; - screen_info->blue_size = fb->blue_mask_size; - screen_info->blue_pos = fb->blue_mask_shift; - - screen_info->orig_video_isVGA = VIDEO_TYPE_EFI; +const char *verify_kernel(struct boot_param *p) { + struct linux_header *header = p->kernel_base; - EFI_GUID screen_info_table_guid = {0xe03fc20a, 0x85dc, 0x406e, {0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95}}; - EFI_STATUS ret = gBS->InstallConfigurationTable(&screen_info_table_guid, screen_info); + if (header->magic2 != LINUX_HEADER_MAGIC2) { + return "kernel header magic does not match"; + } - if (ret != EFI_SUCCESS) { - panic(true, "linux: failed to install screen info configuration table: '%x'", ret); + // riscv-specific version requirements +#if defined(__riscv) + printv("linux: boot protocol version %d.%d\n", + LINUX_HEADER_MAJOR_VER(header->version), + LINUX_HEADER_MINOR_VER(header->version)); + if (LINUX_HEADER_MAJOR_VER(header->version) == 0 + && LINUX_HEADER_MINOR_VER(header->version) < 2) { + return "linux: protocols < 0.2 are not supported"; } +#endif + + return NULL; } -void *prepare_device_tree_blob(char *config, char *cmdline) { - void *dtb = NULL; +void load_files(struct boot_param *p, char *config) { char *dtb_path = config_get_value(config, 0, "DTB_PATH"); if (dtb_path) { @@ -90,13 +124,32 @@ void *prepare_device_tree_blob(char *config, char *cmdline) { if ((dtb_file = uri_open(dtb_path)) == NULL) panic(true, "linux: Failed to open device tree blob with path `%#`. Is the path correct?", dtb_path); - dtb = freadall(dtb_file, MEMMAP_BOOTLOADER_RECLAIMABLE); + p->dtb = freadall(dtb_file, MEMMAP_BOOTLOADER_RECLAIMABLE); fclose(dtb_file); - } else { - // Hopefully 4K should be enough (mainly depends on the length of cmdline). - dtb = get_device_tree_blob(0x1000); } + char *module_path = config_get_value(config, 0, "MODULE_PATH"); + if (module_path) { + print("linux: Loading module `%#`...\n", 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); + } + + p->module_size = module_file->size; + p->module_base = ext_mem_alloc_type_aligned( + ALIGN_UP(p->module_size, 4096), + MEMMAP_KERNEL_AND_MODULES, 4096); + + fread(module_file, p->module_base, 0, p->module_size); + fclose(module_file); + printv("linux: loaded module `%s` at %x, size %u\n", module_path, p->module_base, p->module_size); + } +} + +void prepare_device_tree_blob(struct boot_param *p) { + void *dtb = p->dtb; int ret; // Delete all /memory@... nodes. Linux will use the given UEFI memory map @@ -118,61 +171,96 @@ void *prepare_device_tree_blob(char *config, char *cmdline) { } } - // Load an initrd if requested and add it to the device tree. - char *module_path = config_get_value(config, 0, "MODULE_PATH"); - if (module_path) { - print("linux: Loading module `%#`...\n", 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 (p->module_base) { + ret = fdt_set_chosen_uint64(dtb, "linux,initrd-start", (uint64_t)p->module_base); if (ret < 0) { panic(true, "linux: cannot set initrd parameter: '%s'", fdt_strerror(ret)); } - ret = fdt_set_chosen_uint64(dtb, "linux,initrd-end", (uint64_t)(module_base + module_size)); + ret = fdt_set_chosen_uint64(dtb, "linux,initrd-end", (uint64_t)(p->module_base + p->module_size)); if (ret < 0) { panic(true, "linux: cannot set initrd parameter: '%s'", fdt_strerror(ret)); } } - size_t req_width = 0, req_height = 0, req_bpp = 0; + // Set the kernel command line arguments. + ret = fdt_set_chosen_string(dtb, "bootargs", p->cmdline); + if (ret < 0) { + panic(true, "linux: failed to set bootargs: '%s'", fdt_strerror(ret)); + } - char *resolution = config_get_value(config, 0, "RESOLUTION"); - if (resolution != NULL) { - parse_resolution(&req_width, &req_height, &req_bpp, resolution); + // Tell Linux about the UEFI memory map and system table. + ret = fdt_set_chosen_uint64(dtb, "linux,uefi-system-table", (uint64_t)gST); + if (ret < 0) { + panic(true, "linux: failed to set UEFI system table pointer: '%s'", fdt_strerror(ret)); } - struct fb_info *fbs; - size_t fbs_count; + // This property is not required by mainline Linux, but is required by + // Debian (and derivative) kernels, because Debian has a patch that adds + // this flag, and the existing logic that deals with it will just outright + // fail if any of the properties is missing. We don't care about Debian's + // hardening or whatever, so just always report that secure boot is off. + ret = fdt_set_chosen_uint32(dtb, "linux,uefi-secure-boot", 0); + if (ret < 0) { + panic(true, "linux: failed to set UEFI secure boot state: '%s'", fdt_strerror(ret)); + } +} - term_notready(); +void add_framebuffer(struct fb_info *fb) { + struct screen_info *screen_info = ext_mem_alloc(sizeof(struct screen_info)); - fb_init(&fbs, &fbs_count, req_width, req_height, req_bpp); + screen_info->capabilities = VIDEO_CAPABILITY_64BIT_BASE | VIDEO_CAPABILITY_SKIP_QUIRKS; + screen_info->flags = VIDEO_FLAGS_NOCURSOR; + screen_info->lfb_base = (uint32_t)fb->framebuffer_addr; + screen_info->ext_lfb_base = (uint32_t)(fb->framebuffer_addr >> 32); + screen_info->lfb_size = fb->framebuffer_pitch * fb->framebuffer_height; + screen_info->lfb_width = fb->framebuffer_width; + screen_info->lfb_height = fb->framebuffer_height; + screen_info->lfb_depth = fb->framebuffer_bpp; + screen_info->lfb_linelength = fb->framebuffer_pitch; + screen_info->red_size = fb->red_mask_size; + screen_info->red_pos = fb->red_mask_shift; + screen_info->green_size = fb->green_mask_size; + screen_info->green_pos = fb->green_mask_shift; + screen_info->blue_size = fb->blue_mask_size; + screen_info->blue_pos = fb->blue_mask_shift; + + screen_info->orig_video_isVGA = VIDEO_TYPE_EFI; + + EFI_GUID screen_info_table_guid = {0xe03fc20a, 0x85dc, 0x406e, {0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95}}; + EFI_STATUS ret = gBS->InstallConfigurationTable(&screen_info_table_guid, screen_info); - // TODO(qookie): Let the user pick a framebuffer if there's > 1 - if (fbs_count > 0) { - add_framebuffer(&fbs[0]); + if (ret != EFI_SUCCESS) { + panic(true, "linux: failed to install screen info configuration table: '%x'", ret); } +} - // Set the kernel command line arguments. - ret = fdt_set_chosen_string(dtb, "bootargs", cmdline); - if (ret < 0) { - panic(true, "linux: failed to set bootargs: '%s'", fdt_strerror(ret)); +void prepare_efi_tables(struct boot_param *p, char *config) { + (void)p; + EFI_STATUS ret = 0; + + { + size_t req_width = 0, req_height = 0, req_bpp = 0; + + char *resolution = config_get_value(config, 0, "RESOLUTION"); + if (resolution != NULL) { + parse_resolution(&req_width, &req_height, &req_bpp, resolution); + } + + struct fb_info *fbs; + size_t fbs_count; + + term_notready(); + + fb_init(&fbs, &fbs_count, req_width, req_height, req_bpp); + + // TODO(qookie): Let the user pick a framebuffer if there's > 1 + if (fbs_count > 0) { + add_framebuffer(&fbs[0]); + } } + { struct linux_efi_memreserve *rsv = ext_mem_alloc(sizeof(struct linux_efi_memreserve)); @@ -188,61 +276,154 @@ void *prepare_device_tree_blob(char *config, char *cmdline) { } } - efi_exit_boot_services(); + if (p->module_base) { + struct linux_efi_initrd *initrd_table; - // Tell Linux about the UEFI memory map and system table. - ret = fdt_set_chosen_uint64(dtb, "linux,uefi-system-table", (uint64_t)gST); - if (ret < 0) { - panic(true, "linux: failed to set UEFI system table pointer: '%s'", fdt_strerror(ret)); - } + ret = gBS->AllocatePool(EfiLoaderData, sizeof(*initrd_table), (void **)&initrd_table); + if (ret != EFI_SUCCESS) { + panic(true, "linux: failed to allocate Linux initrd table"); + } - ret = fdt_set_chosen_uint64(dtb, "linux,uefi-mmap-start", (uint64_t)efi_mmap); - if (ret < 0) { - panic(true, "linux: failed to set UEFI memory map pointer: '%s'", fdt_strerror(ret)); - } + initrd_table->base = (UINTN)p->module_base; + initrd_table->size = p->module_size; - ret = fdt_set_chosen_uint32(dtb, "linux,uefi-mmap-size", efi_mmap_size); - if (ret < 0) { - panic(true, "linux: failed to set UEFI memory map size: '%s'", fdt_strerror(ret)); + EFI_GUID initrd_table_guid = { 0x5568e427, 0x68fc, 0x4f3d, { 0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68}}; + ret = gBS->InstallConfigurationTable(&initrd_table_guid, initrd_table); + if (ret != EFI_SUCCESS) { + panic(true, "linux: failed to install initrd\n"); + } } - ret = fdt_set_chosen_uint32(dtb, "linux,uefi-mmap-desc-size", efi_desc_size); - if (ret < 0) { - panic(true, "linux: failed to set UEFI memory map descriptor size: '%s'", fdt_strerror(ret)); - } + { + EFI_MEMORY_DESCRIPTOR tmp_mmap[1]; + size_t mmap_size = sizeof(tmp_mmap); + UINTN mmap_key = 0; - ret = fdt_set_chosen_uint32(dtb, "linux,uefi-mmap-desc-ver", efi_desc_ver); - if (ret < 0) { - panic(true, "linux: failed to set UEFI memory map descriptor version: '%s'", fdt_strerror(ret)); + gBS->GetMemoryMap(&efi_mmap_size, tmp_mmap, &mmap_key, &efi_desc_size, &efi_desc_ver); + mmap_size += 4096 + sizeof(struct linux_efi_boot_memmap); + + ret = gBS->AllocatePool(EfiLoaderData, efi_mmap_size, (void **)&p->memmap); + if (ret != EFI_SUCCESS) { + panic(true, "linux: failed to allocate UEFI memory map"); + } + + p->memmap->buff_size = mmap_size; + + EFI_GUID memmap_table_guid = { 0x800f683f, 0xd08b, 0x423a, { 0xa2, 0x93, 0x96, 0x5c, 0x3c, 0x6f, 0xe2, 0xb4}}; + ret = gBS->InstallConfigurationTable(&memmap_table_guid, p->memmap); + if (ret != EFI_SUCCESS) { + panic(true, "linux: failed to install UEFI memory map"); + } } - // This property is not required by mainline Linux, but is required by - // Debian (and derivative) kernels, because Debian has a patch that adds - // this flag, and the existing logic that deals with it will just outright - // fail if any of the properties is missing. We don't care about Debian's - // hardening or whatever, so just always report that secure boot is off. - ret = fdt_set_chosen_uint32(dtb, "linux,uefi-secure-boot", 0); - if (ret < 0) { - panic(true, "linux: failed to set UEFI secure boot state: '%s'", fdt_strerror(ret)); + efi_exit_boot_services(); +} + +void prepare_mmap(struct boot_param *p) { + { + void *dtb = p->dtb; + int ret = fdt_set_chosen_uint64(dtb, "linux,uefi-mmap-start", (uint64_t)efi_mmap); + if (ret < 0) { + panic(true, "linux: failed to set UEFI memory map pointer: '%s'", fdt_strerror(ret)); + } + + ret = fdt_set_chosen_uint32(dtb, "linux,uefi-mmap-size", efi_mmap_size); + if (ret < 0) { + panic(true, "linux: failed to set UEFI memory map size: '%s'", fdt_strerror(ret)); + } + + ret = fdt_set_chosen_uint32(dtb, "linux,uefi-mmap-desc-size", efi_desc_size); + if (ret < 0) { + panic(true, "linux: failed to set UEFI memory map descriptor size: '%s'", fdt_strerror(ret)); + } + + ret = fdt_set_chosen_uint32(dtb, "linux,uefi-mmap-desc-ver", efi_desc_ver); + if (ret < 0) { + panic(true, "linux: failed to set UEFI memory map descriptor version: '%s'", fdt_strerror(ret)); + } } + p->memmap->map_size = efi_mmap_size; + p->memmap->desc_size = efi_desc_size; + p->memmap->desc_ver = efi_desc_ver; + p->memmap->map_key = efi_mmap_key; + size_t efi_mmap_entry_count = efi_mmap_size / efi_desc_size; for (size_t i = 0; i < efi_mmap_entry_count; i++) { EFI_MEMORY_DESCRIPTOR *entry = (void *)efi_mmap + i * efi_desc_size; - if (entry->Attribute & EFI_MEMORY_RUNTIME) - entry->VirtualStart = entry->PhysicalStart; + if (entry->Attribute & EFI_MEMORY_RUNTIME) { + // LoongArch kernel requires the virtual address stays in the + // privileged, direct-mapped window + #if defined(__loongarch__) + entry->VirtualStart = entry->PhysicalStart | (0x8ULL << 60); + #else + entry->VirtualStart = entry->PhysicalStart; + #endif + } } + memcpy(&p->memmap->descs, efi_mmap, efi_mmap_size); + EFI_STATUS status = gRT->SetVirtualAddressMap(efi_mmap_size, efi_desc_size, efi_desc_ver, efi_mmap); if (status != EFI_SUCCESS) { panic(false, "linux: failed to set UEFI virtual address map: '%x'", status); } +} - return dtb; +noreturn void jump_to_kernel(struct boot_param *p) { +#if defined(__riscv) + printv("linux: bsp hart %d, device tree blob at %x\n", bsp_hartid, p->dtb); + + void (*kernel_entry)(uint64_t hartid, uint64_t dtb) = p->kernel_base; + asm ("csrci sstatus, 0x2\n\t" + "csrw sie, zero\n\t"); + kernel_entry(bsp_hartid, (uint64_t)p->dtb); +#elif defined(__aarch64__) + printv("linux: device tree blob at %x\n", p->dtb); + + void (*kernel_entry)(uint64_t dtb, uint64_t res0, uint64_t res1, uint64_t res2) = p->kernel_base; + asm ("msr daifset, 0xF"); + kernel_entry((uint64_t)p->dtb, 0, 0, 0); +#elif defined(__loongarch__) +// LoongArch kernel used to store virtual address in header.kernel_entry +// clearing the high 16bits ensures compatibility +#define TO_PHYS(addr) ((addr) & ((1ULL << 48) - 1)) +#define CSR_DMW_PLV0 1ULL +#define CSR_DMW0_VSEG 0x8000ULL +#define CSR_DMW0_BASE (CSR_DMW0_VSEG << 48) +#define CSR_DMW0_INIT (CSR_DMW0_BASE | CSR_DMW_PLV0) +#define CSR_DMW1_MAT (1 << 4) +#define CSR_DMW1_VSEG 0x9000ULL +#define CSR_DMW1_BASE (CSR_DMW1_VSEG << 48) +#define CSR_DMW1_INIT (CSR_DMW1_BASE | CSR_DMW1_MAT | CSR_DMW_PLV0) +#define CSR_DMW2_VSEG 0xa000ULL +#define CSR_DMW2_MAT (2 << 4) +#define CSR_DMW2_BASE (CSR_DMW2_VSEG << 48) +#define CSR_DMW2_INIT (CSR_DMW2_BASE | CSR_DMW2_MAT | CSR_DMW_PLV0) +#define CSR_DMW3_INIT 0 + + struct linux_header *header = p->kernel_base; + void (*kernel_entry)(uint64_t efi_boot, uint64_t cmdline, uint64_t st); + kernel_entry = p->kernel_base + (TO_PHYS(header->kernel_entry) - header->load_offset); + + asm volatile ("csrxchg $r0, %0, 0x0" :: "r" (0x4) : "memory"); + asm volatile ("csrwr %0, 0x180" :: "r" (CSR_DMW0_INIT) : "memory"); + asm volatile ("csrwr %0, 0x181" :: "r" (CSR_DMW1_INIT) : "memory"); + asm volatile ("csrwr %0, 0x182" :: "r" (CSR_DMW2_INIT) : "memory"); + asm volatile ("csrwr %0, 0x183" :: "r" (CSR_DMW3_INIT) : "memory"); + kernel_entry(1, (uint64_t)p->cmdline, (uint64_t)gST); +#endif + __builtin_unreachable(); } noreturn void linux_load(char *config, char *cmdline) { + struct boot_param p; + memset(&p, 0, sizeof(p)); + p.cmdline = cmdline; + // Hopefully 4K should be enough (mainly depends on the length of cmdline) + p.dtb = get_device_tree_blob(0x1000); + struct file_handle *kernel_file; char *kernel_path = config_get_value(config, 0, "KERNEL_PATH"); @@ -256,52 +437,27 @@ noreturn void linux_load(char *config, char *cmdline) { 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"); - } - - // Version fields are RV-specific -#if defined(__riscv) - printv("linux: boot protocol version %d.%d\n", - LINUX_HEADER_MAJOR_VER(header.version), - LINUX_HEADER_MINOR_VER(header.version)); - if (LINUX_HEADER_MAJOR_VER(header.version) == 0 - && LINUX_HEADER_MINOR_VER(header.version) < 2) { - panic(true, "linux: protocols < 0.2 are not supported"); - } -#endif - - size_t kernel_size = kernel_file->size; - void *kernel_base = ext_mem_alloc_type_aligned( - ALIGN_UP(kernel_size, 4096), + p.kernel_size = kernel_file->size; + p.kernel_base = ext_mem_alloc_type_aligned( + ALIGN_UP(p.kernel_size, 4096), MEMMAP_KERNEL_AND_MODULES, 2 * 1024 * 1024); - fread(kernel_file, kernel_base, 0, kernel_size); + fread(kernel_file, p.kernel_base, 0, p.kernel_size); fclose(kernel_file); - printv("linux: loaded kernel `%s` at %x, size %u\n", kernel_path, kernel_base, kernel_size); + printv("linux: loaded kernel `%s` at %x, size %u\n", kernel_path, p.kernel_base, p.kernel_size); - void *dtb = prepare_device_tree_blob(config, cmdline); - if (!dtb) { - panic(true, "linux: failed to prepare the device tree blob"); - } + const char *reason = verify_kernel(&p); + if (reason) + panic(true, "linux: invalid kernel image: %s", reason); -#if defined(__riscv) - printv("linux: bsp hart %d, device tree blob at %x\n", bsp_hartid, dtb); + load_files(&p, config); - 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); -#elif defined(__aarch64__) - printv("linux: device tree blob at %x\n", dtb); + prepare_device_tree_blob(&p); - void (*kernel_entry)(uint64_t dtb, uint64_t res0, uint64_t res1, uint64_t res2) = kernel_base; - asm ("msr daifset, 0xF"); - kernel_entry((uint64_t)dtb, 0, 0, 0); -#endif - __builtin_unreachable(); + prepare_efi_tables(&p, config); + + prepare_mmap(&p); + + jump_to_kernel(&p); } #endif