diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 58b4208b0..21ad0dd7c 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1531,6 +1531,7 @@ module = { i386_xen = lib/i386/xen/relocator.S; x86_64_xen = lib/x86_64/xen/relocator.S; xen = lib/i386/relocator_common_c.c; + x86_64_efi = lib/x86_64/efi/relocator.c; extra_dist = lib/i386/relocator_common.S; extra_dist = kern/powerpc/cache_flush.S; diff --git a/grub-core/lib/i386/relocator64.S b/grub-core/lib/i386/relocator64.S index e4648d818..75725cf75 100644 --- a/grub-core/lib/i386/relocator64.S +++ b/grub-core/lib/i386/relocator64.S @@ -73,6 +73,14 @@ VARIABLE(grub_relocator64_rsp) movq %rax, %rsp + /* + * Here is grub_relocator64_efi_start() entry point. + * Following code is shared between grub_relocator64_efi_start() + * and grub_relocator64_start(). + * + * Think twice before changing anything below!!! + */ +VARIABLE(grub_relocator64_efi_start) /* mov imm64, %rax */ .byte 0x48 .byte 0xb8 @@ -120,6 +128,9 @@ LOCAL(jump_addr): VARIABLE(grub_relocator64_rip) .quad 0 + /* Here grub_relocator64_efi_start() ends. Ufff... */ +VARIABLE(grub_relocator64_efi_end) + #ifndef __x86_64__ .p2align 4 LOCAL(gdt): diff --git a/grub-core/lib/x86_64/efi/relocator.c b/grub-core/lib/x86_64/efi/relocator.c new file mode 100644 index 000000000..3caef7a40 --- /dev/null +++ b/grub-core/lib/x86_64/efi/relocator.c @@ -0,0 +1,80 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 Free Software Foundation, Inc. + * Copyright (C) 2016 Oracle and/or its affiliates. All rights reserved. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include + +#include +#include + +#include +#include + +extern grub_uint64_t grub_relocator64_rax; +extern grub_uint64_t grub_relocator64_rbx; +extern grub_uint64_t grub_relocator64_rcx; +extern grub_uint64_t grub_relocator64_rdx; +extern grub_uint64_t grub_relocator64_rip; +extern grub_uint64_t grub_relocator64_rsi; + +extern grub_uint8_t grub_relocator64_efi_start; +extern grub_uint8_t grub_relocator64_efi_end; + +#define RELOCATOR_SIZEOF(x) (&grub_relocator##x##_end - &grub_relocator##x##_start) + +grub_err_t +grub_relocator64_efi_boot (struct grub_relocator *rel, + struct grub_relocator64_efi_state state) +{ + grub_err_t err; + void *relst; + grub_relocator_chunk_t ch; + + /* + * 64-bit relocator code may live above 4 GiB quite well. + * However, I do not want ask for problems. Just in case. + */ + err = grub_relocator_alloc_chunk_align (rel, &ch, 0, + 0x100000000 - RELOCATOR_SIZEOF (64_efi), + RELOCATOR_SIZEOF (64_efi), 16, + GRUB_RELOCATOR_PREFERENCE_NONE, 1); + if (err) + return err; + + /* Do not touch %rsp! It points to EFI created stack. */ + grub_relocator64_rax = state.rax; + grub_relocator64_rbx = state.rbx; + grub_relocator64_rcx = state.rcx; + grub_relocator64_rdx = state.rdx; + grub_relocator64_rip = state.rip; + grub_relocator64_rsi = state.rsi; + + grub_memmove (get_virtual_current_address (ch), &grub_relocator64_efi_start, + RELOCATOR_SIZEOF (64_efi)); + + err = grub_relocator_prepare_relocs (rel, get_physical_target_address (ch), + &relst, NULL); + if (err) + return err; + + ((void (*) (void)) relst) (); + + /* Not reached. */ + return GRUB_ERR_NONE; +} diff --git a/grub-core/loader/multiboot.c b/grub-core/loader/multiboot.c index 73aa0aa12..18038fd31 100644 --- a/grub-core/loader/multiboot.c +++ b/grub-core/loader/multiboot.c @@ -118,6 +118,48 @@ grub_multiboot_set_video_mode (void) return err; } +#ifdef GRUB_MACHINE_EFI +#ifdef __x86_64__ +#define grub_relocator_efi_boot grub_relocator64_efi_boot +#define grub_relocator_efi_state grub_relocator64_efi_state +#endif +#endif + +#ifdef grub_relocator_efi_boot +static void +efi_boot (struct grub_relocator *rel, + grub_uint32_t target) +{ + struct grub_relocator_efi_state state_efi = MULTIBOOT_EFI_INITIAL_STATE; + + state_efi.MULTIBOOT_EFI_ENTRY_REGISTER = grub_multiboot_payload_eip; + state_efi.MULTIBOOT_EFI_MBI_REGISTER = target; + + grub_relocator_efi_boot (rel, state_efi); +} +#else +#define grub_efi_is_finished 1 +static void +efi_boot (struct grub_relocator *rel __attribute__ ((unused)), + grub_uint32_t target __attribute__ ((unused))) +{ +} +#endif + +#if defined (__i386__) || defined (__x86_64__) +static void +normal_boot (struct grub_relocator *rel, struct grub_relocator32_state state) +{ + grub_relocator32_boot (rel, state, 0); +} +#else +static void +normal_boot (struct grub_relocator *rel, struct grub_relocator32_state state) +{ + grub_relocator32_boot (rel, state); +} +#endif + static grub_err_t grub_multiboot_boot (void) { @@ -131,11 +173,10 @@ grub_multiboot_boot (void) if (err) return err; -#if defined (__i386__) || defined (__x86_64__) - grub_relocator32_boot (grub_multiboot_relocator, state, 0); -#else - grub_relocator32_boot (grub_multiboot_relocator, state); -#endif + if (grub_efi_is_finished) + normal_boot (grub_multiboot_relocator, state); + else + efi_boot (grub_multiboot_relocator, state.MULTIBOOT_MBI_REGISTER); /* Not reached. */ return GRUB_ERR_NONE; diff --git a/grub-core/loader/multiboot_mbi2.c b/grub-core/loader/multiboot_mbi2.c index f147d674d..a3dca9011 100644 --- a/grub-core/loader/multiboot_mbi2.c +++ b/grub-core/loader/multiboot_mbi2.c @@ -107,8 +107,8 @@ grub_multiboot_load (grub_file_t file, const char *filename) grub_err_t err; struct multiboot_header_tag *tag; struct multiboot_header_tag_address *addr_tag = NULL; - int entry_specified = 0; - grub_addr_t entry = 0; + int entry_specified = 0, efi_entry_specified = 0; + grub_addr_t entry = 0, efi_entry = 0; grub_uint32_t console_required = 0; struct multiboot_header_tag_framebuffer *fbtag = NULL; int accepted_consoles = GRUB_MULTIBOOT_CONSOLE_EGA_TEXT; @@ -192,6 +192,13 @@ grub_multiboot_load (grub_file_t file, const char *filename) entry = ((struct multiboot_header_tag_entry_address *) tag)->entry_addr; break; + case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64: +#if defined (GRUB_MACHINE_EFI) && defined (__x86_64__) + efi_entry_specified = 1; + efi_entry = ((struct multiboot_header_tag_entry_address *) tag)->entry_addr; +#endif + break; + case MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS: if (!(((struct multiboot_header_tag_console_flags *) tag)->console_flags & MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED)) @@ -211,7 +218,9 @@ grub_multiboot_load (grub_file_t file, const char *filename) break; case MULTIBOOT_HEADER_TAG_EFI_BS: +#ifdef GRUB_MACHINE_EFI keep_bs = 1; +#endif break; default: @@ -224,7 +233,7 @@ grub_multiboot_load (grub_file_t file, const char *filename) break; } - if (addr_tag && !entry_specified) + if (addr_tag && !entry_specified && !(keep_bs && efi_entry_specified)) { grub_free (buffer); return grub_error (GRUB_ERR_UNKNOWN_OS, @@ -287,7 +296,9 @@ grub_multiboot_load (grub_file_t file, const char *filename) } } - if (entry_specified) + if (keep_bs && efi_entry_specified) + grub_multiboot_payload_eip = efi_entry; + else if (entry_specified) grub_multiboot_payload_eip = entry; if (fbtag) diff --git a/include/grub/i386/multiboot.h b/include/grub/i386/multiboot.h index a1b94883d..807a1de27 100644 --- a/include/grub/i386/multiboot.h +++ b/include/grub/i386/multiboot.h @@ -30,6 +30,17 @@ #define MULTIBOOT_MBI_REGISTER ebx #define MULTIBOOT_ARCHITECTURE_CURRENT MULTIBOOT_ARCHITECTURE_I386 +#ifdef GRUB_MACHINE_EFI +#ifdef __x86_64__ +#define MULTIBOOT_EFI_INITIAL_STATE { .rax = MULTIBOOT_BOOTLOADER_MAGIC, \ + .rcx = 0, \ + .rdx = 0, \ + } +#define MULTIBOOT_EFI_ENTRY_REGISTER rip +#define MULTIBOOT_EFI_MBI_REGISTER rbx +#endif +#endif + #define MULTIBOOT_ELF32_MACHINE EM_386 #define MULTIBOOT_ELF64_MACHINE EM_X86_64 diff --git a/include/grub/i386/relocator.h b/include/grub/i386/relocator.h index 5f89a7ec8..2a56c3b54 100644 --- a/include/grub/i386/relocator.h +++ b/include/grub/i386/relocator.h @@ -65,6 +65,20 @@ struct grub_relocator64_state grub_addr_t cr3; }; +#ifdef GRUB_MACHINE_EFI +#ifdef __x86_64__ +struct grub_relocator64_efi_state +{ + grub_uint64_t rax; + grub_uint64_t rbx; + grub_uint64_t rcx; + grub_uint64_t rdx; + grub_uint64_t rip; + grub_uint64_t rsi; +}; +#endif +#endif + grub_err_t grub_relocator16_boot (struct grub_relocator *rel, struct grub_relocator16_state state); @@ -76,4 +90,11 @@ grub_err_t grub_relocator64_boot (struct grub_relocator *rel, struct grub_relocator64_state state, grub_addr_t min_addr, grub_addr_t max_addr); +#ifdef GRUB_MACHINE_EFI +#ifdef __x86_64__ +grub_err_t grub_relocator64_efi_boot (struct grub_relocator *rel, + struct grub_relocator64_efi_state state); +#endif +#endif + #endif /* ! GRUB_RELOCATOR_CPU_HEADER */ diff --git a/include/multiboot2.h b/include/multiboot2.h index 8b8c1568a..46e7b7182 100644 --- a/include/multiboot2.h +++ b/include/multiboot2.h @@ -69,6 +69,7 @@ #define MULTIBOOT_HEADER_TAG_FRAMEBUFFER 5 #define MULTIBOOT_HEADER_TAG_MODULE_ALIGN 6 #define MULTIBOOT_HEADER_TAG_EFI_BS 7 +#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64 9 #define MULTIBOOT_ARCHITECTURE_I386 0 #define MULTIBOOT_ARCHITECTURE_MIPS32 4