protos/linux: Add optional support for 64-bit boot protocol
diff --git a/common/lib/uri.c b/common/lib/uri.c
index 2d0296d9..bcc05d00 100644
--- a/common/lib/uri.c
+++ b/common/lib/uri.c
@@ -206,6 +206,10 @@ static struct file_handle *uri_boot_dispatch(char *s_part, char *path) {
return fopen(volume, path);
}
+#if defined (UEFI) && defined (__x86_64__)
+bool uri_open_allow_high = false;
+#endif
+
struct file_handle *uri_open(char *uri) {
struct file_handle *ret;
@@ -238,7 +242,12 @@ struct file_handle *uri_open(char *uri) {
if (hash != NULL && ret != NULL) {
uint8_t out_buf[BLAKE2B_OUT_BYTES];
+#if defined (UEFI) && defined (__x86_64__)
+ void *file_buf = freadall_mode(ret, MEMMAP_BOOTLOADER_RECLAIMABLE, uri_open_allow_high);
+ uri_open_allow_high = false;
+#else
void *file_buf = freadall(ret, MEMMAP_BOOTLOADER_RECLAIMABLE);
+#endif
blake2b(out_buf, file_buf, ret->size);
uint8_t hash_buf[BLAKE2B_OUT_BYTES];
diff --git a/common/lib/uri.h b/common/lib/uri.h
index d15a5494..8691df91 100644
--- a/common/lib/uri.h
+++ b/common/lib/uri.h
@@ -4,6 +4,10 @@
#include <stdbool.h>
#include <fs/file.h>
+#if defined (UEFI) && defined (__x86_64__)
+extern bool uri_open_allow_high;
+#endif
+
bool uri_resolve(char *uri, char **resource, char **root, char **path, char **hash);
struct file_handle *uri_open(char *uri);
diff --git a/common/protos/linux.c b/common/protos/linux.c
index b5c01bb5..1eee9a9b 100644
--- a/common/protos/linux.c
+++ b/common/protos/linux.c
@@ -21,6 +21,9 @@
#include <drivers/gop.h>
noreturn void linux_spinup(void *entry, void *boot_params);
+#if defined (UEFI) && defined (__x86_64__)
+ noreturn void linux_spinup64(void *entry, void *boot_params);
+#endif
// The following definitions and struct were copied and adapted from Linux
// kernel headers released under GPL-2.0 WITH Linux-syscall-note
@@ -382,14 +385,6 @@ noreturn void linux_load(char *config, char *cmdline) {
///////////////////////////////////////
// Modules
///////////////////////////////////////
-
- uint32_t modules_mem_base;
- if (setup_header->version <= 0x202 || setup_header->initrd_addr_max == 0) {
- modules_mem_base = 0x38000000;
- } else {
- modules_mem_base = setup_header->initrd_addr_max + 1;
- }
-
size_t size_of_all_modules = 0;
for (size_t i = 0; ; i++) {
@@ -398,6 +393,9 @@ noreturn void linux_load(char *config, char *cmdline) {
break;
struct file_handle *module;
+#if defined (UEFI) && defined (__x86_64__)
+ uri_open_allow_high = true;
+#endif
if ((module = uri_open(module_path)) == NULL)
panic(true, "linux: Failed to open module with path `%s`. Is the path correct?", module_path);
@@ -406,6 +404,24 @@ noreturn void linux_load(char *config, char *cmdline) {
fclose(module);
}
+ uintptr_t modules_mem_base;
+
+#if defined (UEFI) && defined (__x86_64__)
+ if ((setup_header->xloadflags & 3) == 3) {
+ modules_mem_base = (uintptr_t)ext_mem_alloc_type_aligned_mode(
+ size_of_all_modules,
+ MEMMAP_BOOTLOADER_RECLAIMABLE,
+ 0x100000,
+ true
+ );
+ } else {
+#endif
+ if (setup_header->version <= 0x202 || setup_header->initrd_addr_max == 0) {
+ modules_mem_base = 0x38000000;
+ } else {
+ modules_mem_base = setup_header->initrd_addr_max + 1;
+ }
+
modules_mem_base -= size_of_all_modules;
modules_mem_base = ALIGN_DOWN(modules_mem_base, 0x100000);
@@ -420,8 +436,11 @@ noreturn void linux_load(char *config, char *cmdline) {
modules_mem_base -= 0x100000;
}
+#if defined (UEFI) && defined (__x86_64__)
+ }
+#endif
- size_t _modules_mem_base = modules_mem_base;
+ uintptr_t _modules_mem_base = modules_mem_base;
for (size_t i = 0; ; i++) {
char *module_path = config_get_value(config, i, "MODULE_PATH");
if (module_path == NULL)
@@ -430,17 +449,29 @@ noreturn void linux_load(char *config, char *cmdline) {
print("linux: Loading module `%#`...\n", module_path);
struct file_handle *module;
+
+#if defined (UEFI) && defined (__x86_64__)
+ uri_open_allow_high = true;
+#endif
if ((module = uri_open(module_path)) == NULL)
panic(true, "linux: Could not open `%#`", module_path);
fread(module, (void *)_modules_mem_base, 0, module->size);
_modules_mem_base += module->size;
+
+ fclose(module);
}
if (size_of_all_modules != 0) {
setup_header->ramdisk_image = (uint32_t)modules_mem_base;
- setup_header->ramdisk_size = (uint32_t)size_of_all_modules;
+#if defined (UEFI) && defined (__x86_64__)
+ boot_params->ext_ramdisk_image = (uint32_t)(modules_mem_base >> 32);
+#endif
+ setup_header->ramdisk_size = (uint32_t)size_of_all_modules;
+#if defined (UEFI) && defined (__x86_64__)
+ boot_params->ext_ramdisk_size = (uint32_t)(size_of_all_modules >> 32);
+#endif
}
///////////////////////////////////////
@@ -572,6 +603,13 @@ no_fb:;
irq_flush_type = IRQ_PIC_ONLY_FLUSH;
+#if defined (UEFI) && defined (__x86_64__)
+ if ((setup_header->xloadflags & 3) == 3) {
+ flush_irqs();
+ linux_spinup64((void *)kernel_load_addr + 0x200, boot_params);
+ }
+#endif
+
common_spinup(linux_spinup, 2, (uint32_t)kernel_load_addr,
(uint32_t)(uintptr_t)boot_params);
}
diff --git a/common/protos/linux_64.asm_uefi_x86_64 b/common/protos/linux_64.asm_uefi_x86_64
new file mode 100644
index 00000000..bca9b46b
--- /dev/null
+++ b/common/protos/linux_64.asm_uefi_x86_64
@@ -0,0 +1,62 @@
+section .data
+
+align 16
+linux_gdt64:
+ dq 0
+
+ dq 0
+
+ dw 0xffff
+ dw 0x0000
+ db 0x00
+ db 10011011b
+ db 10101111b
+ db 0x00
+
+ dw 0xffff
+ dw 0x0000
+ db 0x00
+ db 10010011b
+ db 10001111b
+ db 0x00
+
+ .end:
+
+align 16
+linux_gdt64_ptr:
+ dw (linux_gdt64.end - linux_gdt64) - 1
+ dq linux_gdt64
+
+section .text
+
+bits 64
+
+global linux_spinup64
+linux_spinup64:
+ cli
+ cld
+
+ lgdt [rel linux_gdt64_ptr]
+
+ lea rbx, [rel .fj]
+ push 0x10
+ push rbx
+ retfq
+
+ .fj:
+ mov eax, 0x18
+ mov ds, eax
+ mov es, eax
+ mov fs, eax
+ mov gs, eax
+ mov ss, eax
+
+ mov rax, rdi
+
+ xor ebp, ebp
+ xor edi, edi
+ xor ebx, ebx
+
+ jmp rax
+
+section .note.GNU-stack noalloc noexec nowrite progbits
