protos/linux_risc: pass memory map and initrd by EFI configuration table
Newer Linux kernel supports passing boot params without a devicetree. In this case, the bootloader should register a Linux-specific EFI configuration table, which contains memory mapping information or initrd address. This is the only option on some new platforms like LoongArch, and is necessary to allow booting Linux kernel without a DTB on aarch64/riscv64. Signed-off-by: Yao Zi <ziyao@disroot.org>
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/protos/linux_risc.c b/common/protos/linux_risc.c
index 6d69863c..694b955a 100644
--- a/common/protos/linux_risc.c
+++ b/common/protos/linux_risc.c
@@ -42,6 +42,20 @@ struct linux_efi_memreserve {
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 {
@@ -51,6 +65,7 @@ struct boot_param {
size_t module_size;
char *cmdline;
void *dtb;
+ struct linux_efi_boot_memmap *memmap;
};
#if defined(__riscv)
@@ -203,7 +218,7 @@ void add_framebuffer(struct fb_info *fb) {
void prepare_efi_tables(struct boot_param *p, char *config) {
(void)p;
- int ret = 0;
+ EFI_STATUS ret = 0;
{
size_t req_width = 0, req_height = 0, req_bpp = 0;
@@ -226,6 +241,7 @@ void prepare_efi_tables(struct boot_param *p, char *config) {
}
}
+
{
struct linux_efi_memreserve *rsv = ext_mem_alloc(sizeof(struct linux_efi_memreserve));
@@ -240,39 +256,90 @@ void prepare_efi_tables(struct boot_param *p, char *config) {
panic(true, "linux: failed to install memory reservation configuration table: '%x'", ret);
}
}
+
+ if (p->module_base) {
+ struct linux_efi_initrd *initrd_table;
+
+ ret = gBS->AllocatePool(EfiLoaderData, sizeof(*initrd_table), (void **)&initrd_table);
+ if (ret != EFI_SUCCESS) {
+ panic(true, "linux: failed to allocate Linux initrd table");
+ }
+
+ initrd_table->base = (UINTN)p->module_base;
+ initrd_table->size = p->module_size;
+
+ 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");
+ }
+ }
+
+ {
+ EFI_MEMORY_DESCRIPTOR tmp_mmap[1];
+ size_t mmap_size = sizeof(tmp_mmap);
+ UINTN mmap_key = 0;
+
+ 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");
+ }
+ }
+
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));
- }
+ {
+ 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-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-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));
+ 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)
+ if (entry->Attribute & EFI_MEMORY_RUNTIME) {
entry->VirtualStart = entry->PhysicalStart;
+ }
}
+ 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);
