#include <grub/relocator.h>
#include <grub/relocator_private.h>
+#include <grub/i386/memory.h>
+
extern grub_uint8_t grub_relocator_forward_start;
extern grub_uint8_t grub_relocator_forward_end;
extern grub_uint8_t grub_relocator_backward_start;
#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