diff --git a/grub-core/lib/i386/relocator_common_c.c b/grub-core/lib/i386/relocator_common_c.c index 7be609b73..c9c5efa72 100644 --- a/grub-core/lib/i386/relocator_common_c.c +++ b/grub-core/lib/i386/relocator_common_c.c @@ -26,6 +26,8 @@ #include #include +#include + extern grub_uint8_t grub_relocator_forward_start; extern grub_uint8_t grub_relocator_forward_end; extern grub_uint8_t grub_relocator_backward_start; @@ -41,20 +43,122 @@ extern grub_size_t grub_relocator_forward_chunk_size; #define RELOCATOR_SIZEOF(x) (&grub_relocator##x##_end - &grub_relocator##x##_start) -grub_size_t grub_relocator_align = 1; grub_size_t grub_relocator_forward_size; grub_size_t grub_relocator_backward_size; +grub_size_t grub_relocator_preamble_size = 0; #ifdef __x86_64__ grub_size_t grub_relocator_jumper_size = 12; #else grub_size_t grub_relocator_jumper_size = 7; #endif +#if defined(__x86_64__) && defined(GRUB_MACHINE_EFI) +grub_size_t grub_relocator_align = 4096; +#else +grub_size_t grub_relocator_align = 1; +#endif + +#if defined(__x86_64__) && defined(GRUB_MACHINE_EFI) + +#define PAGE_PRESENT 1 +#define PAGE_WRITABLE 2 +#define PAGE_USER 4 +#define PAGE_PS 0x80 +#define PAGE_IDX_SIZE 9 +#define PAGE_PS_SHIFT 21 +#define PAGE_NUM_ENTRIES 0x200 +#define PS_PAGE_SIZE 0x200000 + +static grub_uint64_t max_ram_size; + + /* Helper for grub_get_multiboot_mmap_count. */ +static int +max_hook (grub_uint64_t addr, + grub_uint64_t size, + grub_memory_type_t type __attribute__ ((unused)), + void *data __attribute__ ((unused))) +{ + max_ram_size = grub_max (max_ram_size, addr + size); + return 0; +} + +static grub_uint64_t +find_max_size (void) +{ + if (!max_ram_size) + { + /* We need to map the first 4GiB of address space as well as all the + available RAM, so start with 4GiB and increase if we see any RAM + above this. */ + max_ram_size = 1ULL << 32; + + grub_mmap_iterate (max_hook, NULL); + } + + return max_ram_size; +} + +void +grub_cpu_relocator_preamble (void *rels) +{ + grub_uint64_t nentries = (find_max_size () + PS_PAGE_SIZE - 1) >> PAGE_PS_SHIFT; + grub_uint64_t npt2pages = (nentries + PAGE_NUM_ENTRIES - 1) >> PAGE_IDX_SIZE; + grub_uint64_t npt3pages = (npt2pages + PAGE_NUM_ENTRIES - 1) >> PAGE_IDX_SIZE; + grub_uint8_t *p = rels; + grub_uint64_t *pt4 = (grub_uint64_t *) (p + GRUB_PAGE_SIZE); + grub_uint64_t *pt3 = pt4 + PAGE_NUM_ENTRIES; + grub_uint64_t *pt2 = pt3 + (npt3pages << PAGE_IDX_SIZE); + grub_uint64_t *endpreamble = pt2 + (npt2pages << PAGE_IDX_SIZE); + grub_uint64_t i; + + /* movabs $pt4, %rax. */ + *p++ = 0x48; + *p++ = 0xb8; + *(grub_uint64_t *) p = (grub_uint64_t) pt4; + p += 8; + + /* mov %rax, %cr3. */ + *p++ = 0x0f; + *p++ = 0x22; + *p++ = 0xd8; + + /* jmp $endpreamble. */ + *p++ = 0xe9; + *(grub_uint32_t *) p = (grub_uint8_t *) endpreamble - p - 4; + + for (i = 0; i < npt3pages; i++) + pt4[i] = ((grub_uint64_t) pt3 + (i << GRUB_PAGE_SHIFT)) | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER; + + for (i = 0; i < npt2pages; i++) + pt3[i] = ((grub_uint64_t) pt2 + (i << GRUB_PAGE_SHIFT)) | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER; + + for (i = 0; i < (npt2pages << PAGE_IDX_SIZE); i++) + pt2[i] = (i << PAGE_PS_SHIFT) | PAGE_PS | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER; +} + +static void +compute_preamble_size (void) +{ + grub_uint64_t nentries = (find_max_size () + PS_PAGE_SIZE - 1) >> PAGE_PS_SHIFT; + grub_uint64_t npt2pages = (nentries + PAGE_NUM_ENTRIES - 1) >> PAGE_IDX_SIZE; + grub_uint64_t npt3pages = (npt2pages + PAGE_NUM_ENTRIES - 1) >> PAGE_IDX_SIZE; + grub_relocator_preamble_size = (npt2pages + npt3pages + 1 + 1) << GRUB_PAGE_SHIFT; +} + +#else +void +grub_cpu_relocator_preamble (void *rels __attribute__((unused))) +{ +} +#endif void grub_cpu_relocator_init (void) { grub_relocator_forward_size = RELOCATOR_SIZEOF (_forward); grub_relocator_backward_size = RELOCATOR_SIZEOF (_backward); +#if defined(__x86_64__) && defined(GRUB_MACHINE_EFI) + compute_preamble_size (); +#endif } void diff --git a/grub-core/lib/mips/relocator.c b/grub-core/lib/mips/relocator.c index 773f3b769..9f79b40c8 100644 --- a/grub-core/lib/mips/relocator.c +++ b/grub-core/lib/mips/relocator.c @@ -45,6 +45,7 @@ grub_size_t grub_relocator_align = sizeof (grub_uint32_t); grub_size_t grub_relocator_forward_size; grub_size_t grub_relocator_backward_size; grub_size_t grub_relocator_jumper_size = JUMP_SIZEOF + REGW_SIZEOF; +grub_size_t grub_relocator_preamble_size = 0; void grub_cpu_relocator_init (void) @@ -53,6 +54,11 @@ grub_cpu_relocator_init (void) grub_relocator_backward_size = RELOCATOR_SIZEOF(backward); } +void +grub_cpu_relocator_preamble (void *rels __attribute__ ((unused))) +{ +} + static void write_reg (int regn, grub_uint32_t val, void **target) { diff --git a/grub-core/lib/powerpc/relocator.c b/grub-core/lib/powerpc/relocator.c index 15aeb0246..559e04b5a 100644 --- a/grub-core/lib/powerpc/relocator.c +++ b/grub-core/lib/powerpc/relocator.c @@ -43,6 +43,7 @@ grub_size_t grub_relocator_align = sizeof (grub_uint32_t); grub_size_t grub_relocator_forward_size; grub_size_t grub_relocator_backward_size; grub_size_t grub_relocator_jumper_size = JUMP_SIZEOF + REGW_SIZEOF; +grub_size_t grub_relocator_preamble_size = 0; void grub_cpu_relocator_init (void) @@ -51,6 +52,11 @@ grub_cpu_relocator_init (void) grub_relocator_backward_size = RELOCATOR_SIZEOF(backward); } +void +grub_cpu_relocator_preamble (void *rels __attribute__((unused))) +{ +} + static void write_reg (int regn, grub_uint32_t val, void **target) { diff --git a/grub-core/lib/relocator.c b/grub-core/lib/relocator.c index 3306a1bb7..1e1e09704 100644 --- a/grub-core/lib/relocator.c +++ b/grub-core/lib/relocator.c @@ -110,7 +110,7 @@ grub_relocator_new (void) return NULL; ret->postchunks = ~(grub_phys_addr_t) 0; - ret->relocators_size = grub_relocator_jumper_size; + ret->relocators_size = grub_relocator_jumper_size + grub_relocator_preamble_size; grub_dprintf ("relocator", "relocators_size=%lu\n", (unsigned long) ret->relocators_size); return ret; @@ -1605,6 +1605,9 @@ grub_relocator_prepare_relocs (struct grub_relocator *rel, grub_addr_t addr, grub_free (to); } + grub_cpu_relocator_preamble (rels); + rels += grub_relocator_preamble_size; + for (j = 0; j < nchunks; j++) { grub_dprintf ("relocator", "sorted chunk %p->%p, 0x%lx\n", diff --git a/include/grub/relocator_private.h b/include/grub/relocator_private.h index d8e972e01..273add76d 100644 --- a/include/grub/relocator_private.h +++ b/include/grub/relocator_private.h @@ -27,6 +27,7 @@ extern grub_size_t grub_relocator_align; extern grub_size_t grub_relocator_forward_size; extern grub_size_t grub_relocator_backward_size; extern grub_size_t grub_relocator_jumper_size; +extern grub_size_t grub_relocator_preamble_size; void grub_cpu_relocator_init (void); @@ -39,6 +40,7 @@ void grub_cpu_relocator_forward (void *rels, void *src, void *tgt, void grub_cpu_relocator_backward (void *rels, void *src, void *tgt, grub_size_t size); void grub_cpu_relocator_jumper (void *rels, grub_addr_t addr); +void grub_cpu_relocator_preamble (void *rels); /* Remark: GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT_LOG = 1 or 2 aren't supported. */