grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH V2 2/3] Add support for avoiding firmware in relocations


From: Matthew Garrett
Subject: [PATCH V2 2/3] Add support for avoiding firmware in relocations
Date: Mon, 6 Feb 2012 10:53:04 -0500

EFI and OF both support firmware regions which may be in use during loading.
Add support for avoiding these.
---

Updated to allow us to relocate on top of loader data - this should be safe.

 ChangeLog                             |   10 ++++++
 grub-core/lib/efi/relocator.c         |   51 +++++++++++++++++++++++++++++++++
 grub-core/lib/ieee1275/relocator.c    |   27 +++++++++++++++++
 grub-core/lib/relocator.c             |   12 +++++++-
 grub-core/loader/i386/bsd.c           |   19 +++++++-----
 grub-core/loader/i386/bsdXX.c         |    8 ++--
 grub-core/loader/i386/linux.c         |    4 +-
 grub-core/loader/i386/multiboot_mbi.c |    2 +-
 grub-core/loader/i386/pc/freedos.c    |    2 +-
 grub-core/loader/i386/pc/linux.c      |    4 +-
 grub-core/loader/i386/pc/ntldr.c      |    4 +-
 grub-core/loader/mips/linux.c         |    6 ++--
 grub-core/loader/multiboot_elfxx.c    |    2 +-
 grub-core/loader/multiboot_mbi2.c     |    2 +-
 grub-core/loader/xnu.c                |    2 +-
 grub-core/loader/xnu_resume.c         |    2 +-
 include/grub/relocator.h              |    3 +-
 include/grub/relocator_private.h      |    2 +
 18 files changed, 133 insertions(+), 29 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 943efe1..33e5dda 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,15 @@
 2012-02-06  Matthew Garrett  <address@hidden>
 
+       * grub-core/lib/efi/relocator.c (grub_relocator_alloc_chunk_addr):
+       Add argument to fail allocation when target address overlaps
+       firmware regions. All users updated.
+       * grub-core/lib/efi/relocator.c (grub_relocator_firmware_overlaps):
+       New function
+       * grub-core/lib/ieee1275/relocator.c (grub_relocator_firmware_overlaps):
+       Likewise
+
+2012-02-06  Matthew Garrett  <address@hidden>
+
        * include/grub/i386/linux.h (linux_kernel_header): Update to
        boot protocol 2.10.
        (linux_kernel_params): Likewise
diff --git a/grub-core/lib/efi/relocator.c b/grub-core/lib/efi/relocator.c
index 0d346be..9d51a27 100644
--- a/grub-core/lib/efi/relocator.c
+++ b/grub-core/lib/efi/relocator.c
@@ -39,6 +39,57 @@ grub_relocator_firmware_get_max_events (void)
   return 2 * (mmapsize / descriptor_size + 10);
 }
 
+unsigned
+grub_relocator_firmware_overlaps (grub_phys_addr_t target, grub_size_t size)
+{
+  grub_efi_uintn_t mmapsize = 0, desc_size = 0;
+  grub_efi_uint32_t descriptor_version = 0;
+  grub_efi_memory_descriptor_t *descs = NULL;
+  grub_efi_uintn_t key;
+  int overlap = 0;
+  grub_efi_memory_descriptor_t *desc;
+
+  grub_efi_get_memory_map (&mmapsize, NULL, &key, &desc_size,
+                          &descriptor_version);
+  descs = grub_malloc (mmapsize);
+  if (!descs)
+    return 0;
+
+  grub_efi_get_memory_map (&mmapsize, descs, &key, &desc_size,
+                          &descriptor_version);
+
+  for (desc = descs;
+       (char *) desc < ((char *) descs + mmapsize);
+       desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
+    {
+      grub_uint64_t start = desc->physical_start;
+      grub_uint64_t end = desc->physical_start + (desc->num_pages << 12);
+
+      switch (desc->type) {
+      case GRUB_EFI_CONVENTIONAL_MEMORY:
+      case GRUB_EFI_LOADER_CODE:
+      case GRUB_EFI_LOADER_DATA:
+       continue;
+      }
+
+      if (target >= start && target <= end)
+       {
+         overlap = 1;
+         break;
+       }
+
+      if (target + size >= start && target + size <= end)
+       {
+         overlap = 1;
+         break;
+       }
+    }
+
+  grub_free(descs);
+
+  return overlap;
+}
+
 unsigned 
 grub_relocator_firmware_fill_events (struct grub_relocator_mmap_event *events)
 {
diff --git a/grub-core/lib/ieee1275/relocator.c 
b/grub-core/lib/ieee1275/relocator.c
index 021f0ce..28258d7 100644
--- a/grub-core/lib/ieee1275/relocator.c
+++ b/grub-core/lib/ieee1275/relocator.c
@@ -42,6 +42,33 @@ grub_relocator_firmware_get_max_events (void)
   return 2 * counter;
 }
 
+unsigned
+grub_relocator_firmware_overlaps (grub_phys_addr_t target, grub_size_t size)
+{
+  int overlap = 0;
+  auto int NESTED_FUNC_ATTR check (grub_uint64_t addr, grub_uint64_t len,
+                                 grub_memory_type_t type);
+  int NESTED_FUNC_ATTR check (grub_uint64_t addr, grub_uint64_t len,
+                            grub_memory_type_t type)
+  {
+    grub_uint64_t end = addr + len;
+
+    if (type == GRUB_MEMORY_AVAILABLE)
+      return 0;
+
+    if (target >= addr && target <= end)
+      overlap = 1;
+
+    if (target + size >= addr && target + size <= end)
+      overlap = 1;
+
+    return 0;
+  }
+
+  grub_machine_mmap_iterate (check);
+  return overlap;
+}
+
 unsigned 
 grub_relocator_firmware_fill_events (struct grub_relocator_mmap_event *events)
 {
diff --git a/grub-core/lib/relocator.c b/grub-core/lib/relocator.c
index 6630646..7e202a5 100644
--- a/grub-core/lib/relocator.c
+++ b/grub-core/lib/relocator.c
@@ -1203,7 +1203,8 @@ adjust_limits (struct grub_relocator *rel,
 grub_err_t
 grub_relocator_alloc_chunk_addr (struct grub_relocator *rel,
                                 grub_relocator_chunk_t *out,
-                                grub_phys_addr_t target, grub_size_t size)
+                                grub_phys_addr_t target, grub_size_t size,
+                                int avoid_firmware)
 {
   struct grub_relocator_chunk *chunk;
   grub_phys_addr_t min_addr = 0, max_addr;
@@ -1213,6 +1214,15 @@ grub_relocator_alloc_chunk_addr (struct grub_relocator 
*rel,
 
   adjust_limits (rel, &min_addr, &max_addr, target, target);
 
+  if (avoid_firmware)
+    {
+#if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
+      if (grub_relocator_firmware_overlaps(target, size))
+
+      return grub_error(GRUB_ERR_BAD_ARGUMENT, "target overlaps with 
firmware");
+#endif
+    }
+
   for (chunk = rel->chunks; chunk; chunk = chunk->next)
     if ((chunk->target <= target && target < chunk->target + chunk->size)
        || (target <= chunk->target && chunk->target < target + size))
diff --git a/grub-core/loader/i386/bsd.c b/grub-core/loader/i386/bsd.c
index 835f146..f7b89a6 100644
--- a/grub-core/loader/i386/bsd.c
+++ b/grub-core/loader/i386/bsd.c
@@ -606,7 +606,7 @@ grub_freebsd_boot (void)
   {
     grub_relocator_chunk_t ch;
     err = grub_relocator_alloc_chunk_addr (relocator, &ch,
-                                          kern_end, p_size);
+                                          kern_end, p_size, 0);
     if (err)
       return err;
     p = get_virtual_current_address (ch);
@@ -788,7 +788,7 @@ grub_openbsd_boot (void)
     err = grub_relocator_alloc_chunk_addr (relocator, &ch, buf_target,
                                           tag_buf_len
                                           + sizeof (struct 
grub_openbsd_bootargs)
-                                          + 9 * sizeof (grub_uint32_t));
+                                          + 9 * sizeof (grub_uint32_t), 0);
     if (err)
       return err;
     buf0 = get_virtual_current_address (ch);
@@ -1079,7 +1079,8 @@ grub_netbsd_boot (void)
     err = grub_relocator_alloc_chunk_addr (relocator, &ch,
                                           arg_target, tag_buf_len
                                           + sizeof (struct 
grub_netbsd_bootinfo)
-                                          + tag_count * sizeof 
(grub_uint32_t));
+                                          + tag_count * sizeof (grub_uint32_t),
+                                          0);
     if (err)
       return err;
     curarg = get_virtual_current_address (ch);
@@ -1219,7 +1220,8 @@ grub_bsd_load_aout (grub_file_t file, const char 
*filename)
     grub_relocator_chunk_t ch;
 
     err = grub_relocator_alloc_chunk_addr (relocator, &ch,
-                                          kern_start, kern_end - kern_start);
+                                          kern_start, kern_end - kern_start,
+                                          0);
     if (err)
       return err;
     kern_chunk_src = get_virtual_current_address (ch);
@@ -1330,7 +1332,8 @@ grub_bsd_load_elf (grub_elf_t elf)
       if (err)
        return err;
       err = grub_relocator_alloc_chunk_addr (relocator, &ch,
-                                            kern_start, kern_end - kern_start);
+                                            kern_start, kern_end - kern_start,
+                                            0);
       if (err)
        return err;
 
@@ -1373,7 +1376,7 @@ grub_bsd_load_elf (grub_elf_t elf)
        grub_relocator_chunk_t ch;
 
        err = grub_relocator_alloc_chunk_addr (relocator, &ch, kern_start,
-                                              kern_end - kern_start);
+                                              kern_end - kern_start, 0);
        if (err)
          return err;
        kern_chunk_src = get_virtual_current_address (ch);
@@ -1837,7 +1840,7 @@ grub_cmd_freebsd_module (grub_command_t cmd __attribute__ 
((unused)),
   {
     grub_relocator_chunk_t ch;
     err = grub_relocator_alloc_chunk_addr (relocator, &ch, kern_end, 
-                                          file->size);
+                                          file->size, 0);
     if (err)
       goto fail;
     src = get_virtual_current_address (ch);
@@ -1888,7 +1891,7 @@ grub_netbsd_module_load (char *filename, grub_uint32_t 
type)
   {
     grub_relocator_chunk_t ch;
     err = grub_relocator_alloc_chunk_addr (relocator, &ch, kern_end, 
-                                          file->size);
+                                          file->size, 0);
     if (err)
       goto fail;
 
diff --git a/grub-core/loader/i386/bsdXX.c b/grub-core/loader/i386/bsdXX.c
index 7cec7a8..239c633 100644
--- a/grub-core/loader/i386/bsdXX.c
+++ b/grub-core/loader/i386/bsdXX.c
@@ -106,7 +106,7 @@ SUFFIX (grub_freebsd_load_elfmodule_obj) (struct 
grub_relocator *relocator,
   {
     grub_relocator_chunk_t ch;
     err = grub_relocator_alloc_chunk_addr (relocator, &ch,
-                                          module, chunk_size);
+                                          module, chunk_size, 0);
     if (err)
       return err;
     chunk_src = get_virtual_current_address (ch);
@@ -204,7 +204,7 @@ SUFFIX (grub_freebsd_load_elfmodule) (struct grub_relocator 
*relocator,
     grub_relocator_chunk_t ch;
 
     err = grub_relocator_alloc_chunk_addr (relocator, &ch,
-                                          module, chunk_size);
+                                          module, chunk_size, 0);
     if (err)
       return err;
 
@@ -319,7 +319,7 @@ SUFFIX (grub_freebsd_load_elf_meta) (struct grub_relocator 
*relocator,
   {
     grub_relocator_chunk_t ch;
     err = grub_relocator_alloc_chunk_addr (relocator, &ch,
-                                          symtarget, chunk_size);
+                                          symtarget, chunk_size, 0);
     if (err)
       return err;
     sym_chunk = get_virtual_current_address (ch);
@@ -434,7 +434,7 @@ SUFFIX (grub_netbsd_load_elf_meta) (struct grub_relocator 
*relocator,
   {
     grub_relocator_chunk_t ch;
     err = grub_relocator_alloc_chunk_addr (relocator, &ch,
-                                          symtarget, chunk_size);
+                                          symtarget, chunk_size, 0);
     if (err)
       return err;
     sym_chunk = get_virtual_current_address (ch);
diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c
index a5bcb24..67a4533 100644
--- a/grub-core/loader/i386/linux.c
+++ b/grub-core/loader/i386/linux.c
@@ -266,7 +266,7 @@ allocate_pages (grub_size_t prot_size)
     err = grub_relocator_alloc_chunk_addr (relocator, &ch,
                                           real_mode_target,
                                           (real_size + mmap_size 
-                                           + efi_mmap_size));
+                                           + efi_mmap_size), 0);
     if (err)
       goto fail;
     real_mode_mem = get_virtual_current_address (ch);
@@ -278,7 +278,7 @@ allocate_pages (grub_size_t prot_size)
   {
     grub_relocator_chunk_t ch;
     err = grub_relocator_alloc_chunk_addr (relocator, &ch,
-                                          prot_mode_target, prot_size);
+                                          prot_mode_target, prot_size, 0);
     if (err)
       goto fail;
     prot_mode_mem = get_virtual_current_address (ch);
diff --git a/grub-core/loader/i386/multiboot_mbi.c 
b/grub-core/loader/i386/multiboot_mbi.c
index abd4ae9..a95abc9 100644
--- a/grub-core/loader/i386/multiboot_mbi.c
+++ b/grub-core/loader/i386/multiboot_mbi.c
@@ -119,7 +119,7 @@ grub_multiboot_load (grub_file_t file)
 
       err = grub_relocator_alloc_chunk_addr (grub_multiboot_relocator, 
                                             &ch, header->load_addr,
-                                            code_size);
+                                            code_size, 0);
       if (err)
        {
          grub_dprintf ("multiboot_loader", "Error loading aout kludge\n");
diff --git a/grub-core/loader/i386/pc/freedos.c 
b/grub-core/loader/i386/pc/freedos.c
index 1f088e2..24b6dce 100644
--- a/grub-core/loader/i386/pc/freedos.c
+++ b/grub-core/loader/i386/pc/freedos.c
@@ -101,7 +101,7 @@ grub_cmd_freedos (grub_command_t cmd __attribute__ 
((unused)),
   {
     grub_relocator_chunk_t ch;
     err = grub_relocator_alloc_chunk_addr (rel, &ch, GRUB_FREEDOS_SEGMENT << 4,
-                                          kernelsyssize);
+                                          kernelsyssize, 0);
     if (err)
       goto fail;
     kernelsys = get_virtual_current_address (ch);
diff --git a/grub-core/loader/i386/pc/linux.c b/grub-core/loader/i386/pc/linux.c
index 7c4a4be..dee4126 100644
--- a/grub-core/loader/i386/pc/linux.c
+++ b/grub-core/loader/i386/pc/linux.c
@@ -273,7 +273,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
     err = grub_relocator_alloc_chunk_addr (relocator, &ch,
                                           grub_linux_real_target,
                                           GRUB_LINUX_CL_OFFSET
-                                          + maximal_cmdline_size);
+                                          + maximal_cmdline_size, 0);
     if (err)
       return err;
     grub_linux_real_chunk = get_virtual_current_address (ch);
@@ -317,7 +317,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
     grub_relocator_chunk_t ch;
     err = grub_relocator_alloc_chunk_addr (relocator, &ch,
                                           grub_linux_prot_target,
-                                          grub_linux16_prot_size);
+                                          grub_linux16_prot_size, 0);
     if (err)
       return err;
     grub_linux_prot_chunk = get_virtual_current_address (ch);
diff --git a/grub-core/loader/i386/pc/ntldr.c b/grub-core/loader/i386/pc/ntldr.c
index 153b605..f613887 100644
--- a/grub-core/loader/i386/pc/ntldr.c
+++ b/grub-core/loader/i386/pc/ntldr.c
@@ -97,7 +97,7 @@ grub_cmd_ntldr (grub_command_t cmd __attribute__ ((unused)),
   {
     grub_relocator_chunk_t ch;
     err = grub_relocator_alloc_chunk_addr (rel, &ch, 0x7C00,
-                                          GRUB_DISK_SECTOR_SIZE);
+                                          GRUB_DISK_SECTOR_SIZE, 0);
     if (err)
       goto fail;
     bs = get_virtual_current_address (ch);
@@ -124,7 +124,7 @@ grub_cmd_ntldr (grub_command_t cmd __attribute__ ((unused)),
   {
     grub_relocator_chunk_t ch;
     err = grub_relocator_alloc_chunk_addr (rel, &ch, GRUB_NTLDR_SEGMENT << 4,
-                                          ntldrsize);
+                                          ntldrsize, 0);
     if (err)
       goto fail;
     ntldr = get_virtual_current_address (ch);
diff --git a/grub-core/loader/mips/linux.c b/grub-core/loader/mips/linux.c
index 7fda817..8fba174 100644
--- a/grub-core/loader/mips/linux.c
+++ b/grub-core/loader/mips/linux.c
@@ -87,7 +87,7 @@ grub_linux_boot (void)
 
     err = grub_relocator_alloc_chunk_addr (relocator, &ch,
                                           ((16 << 20) - 264),
-                                          grub_strlen (params) + 1 + 8);
+                                          grub_strlen (params) + 1 + 8, 0);
     if (err)
       return err;
     memsize = get_virtual_current_address (ch);
@@ -159,7 +159,7 @@ grub_linux_load32 (grub_elf_t elf, void **extra_mem, 
grub_size_t extra_size)
     grub_relocator_chunk_t ch;
     err = grub_relocator_alloc_chunk_addr (relocator, &ch,
                                           target_addr & 0x1fffffff,
-                                          linux_size);
+                                          linux_size, 0);
     if (err)
       return err;
     playground = get_virtual_current_address (ch);
@@ -214,7 +214,7 @@ grub_linux_load64 (grub_elf_t elf, void **extra_mem, 
grub_size_t extra_size)
     grub_relocator_chunk_t ch;
     err = grub_relocator_alloc_chunk_addr (relocator, &ch,
                                           target_addr & 0x1fffffff,
-                                          linux_size);
+                                          linux_size, 0);
     if (err)
       return err;
     playground = get_virtual_current_address (ch);
diff --git a/grub-core/loader/multiboot_elfxx.c 
b/grub-core/loader/multiboot_elfxx.c
index c6c88f1..9bbc10b 100644
--- a/grub-core/loader/multiboot_elfxx.c
+++ b/grub-core/loader/multiboot_elfxx.c
@@ -102,7 +102,7 @@ CONCAT(grub_multiboot_load_elf, XX) (grub_file_t file, void 
*buffer)
            grub_relocator_chunk_t ch;
            err = grub_relocator_alloc_chunk_addr (grub_multiboot_relocator, 
                                                   &ch, phdr(i)->p_paddr,
-                                                  phdr(i)->p_memsz);
+                                                  phdr(i)->p_memsz, 0);
            if (err)
              {
                grub_dprintf ("multiboot_loader", "Error loading phdr %d\n", i);
diff --git a/grub-core/loader/multiboot_mbi2.c 
b/grub-core/loader/multiboot_mbi2.c
index 9a81afe..15760d0 100644
--- a/grub-core/loader/multiboot_mbi2.c
+++ b/grub-core/loader/multiboot_mbi2.c
@@ -227,7 +227,7 @@ grub_multiboot_load (grub_file_t file)
 
       err = grub_relocator_alloc_chunk_addr (grub_multiboot_relocator, 
                                             &ch, addr_tag->load_addr,
-                                            code_size);
+                                            code_size, 0);
       if (err)
        {
          grub_dprintf ("multiboot_loader", "Error loading aout kludge\n");
diff --git a/grub-core/loader/xnu.c b/grub-core/loader/xnu.c
index ec7c342..5d70145 100644
--- a/grub-core/loader/xnu.c
+++ b/grub-core/loader/xnu.c
@@ -60,7 +60,7 @@ grub_xnu_heap_malloc (int size, void **src, grub_addr_t 
*target)
   
   err = grub_relocator_alloc_chunk_addr (grub_xnu_relocator, &ch,
                                         grub_xnu_heap_target_start
-                                        + grub_xnu_heap_size, size);
+                                        + grub_xnu_heap_size, size, 0);
   if (err)
     return err;
 
diff --git a/grub-core/loader/xnu_resume.c b/grub-core/loader/xnu_resume.c
index d7f9ada..e4fdb95 100644
--- a/grub-core/loader/xnu_resume.c
+++ b/grub-core/loader/xnu_resume.c
@@ -110,7 +110,7 @@ grub_xnu_resume (char *imagename)
   {
     grub_relocator_chunk_t ch;
     err = grub_relocator_alloc_chunk_addr (grub_xnu_relocator, &ch, codedest,
-                                          codesize + GRUB_XNU_PAGESIZE);
+                                          codesize + GRUB_XNU_PAGESIZE, 0);
     if (err)
       {
        grub_file_close (file);
diff --git a/include/grub/relocator.h b/include/grub/relocator.h
index 653a00e..b250512 100644
--- a/include/grub/relocator.h
+++ b/include/grub/relocator.h
@@ -33,7 +33,8 @@ struct grub_relocator *grub_relocator_new (void);
 grub_err_t
 grub_relocator_alloc_chunk_addr (struct grub_relocator *rel,
                                 grub_relocator_chunk_t *out,
-                                grub_phys_addr_t target, grub_size_t size);
+                                grub_phys_addr_t target, grub_size_t size,
+                                int avoid_firmware);
 
 void *
 get_virtual_current_address (grub_relocator_chunk_t in);
diff --git a/include/grub/relocator_private.h b/include/grub/relocator_private.h
index 1c563cb..1948af4 100644
--- a/include/grub/relocator_private.h
+++ b/include/grub/relocator_private.h
@@ -104,6 +104,8 @@ struct grub_relocator_mmap_event
 #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
 int grub_relocator_firmware_alloc_region (grub_phys_addr_t start,
                                          grub_size_t size);
+unsigned grub_relocator_firmware_overlaps (grub_phys_addr_t start,
+                                          grub_size_t size);
 unsigned grub_relocator_firmware_fill_events (struct grub_relocator_mmap_event 
*events);
 unsigned grub_relocator_firmware_get_max_events (void);
 void grub_relocator_firmware_free_region (grub_phys_addr_t start,
-- 
1.7.7.6




reply via email to

[Prev in Thread] Current Thread [Next in Thread]