Re: [RFC PATCH] x86/efi: Allocate e820 buffer before calling efi_exit_boot_service

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

 



On 10/12/2018 3:46 PM, Ard Biesheuvel wrote:
On 9 August 2018 at 16:50, Eric Snowberg <eric.snowberg@xxxxxxxxxx> wrote:
Commit d64934019f6c ("x86/efi: Use efi_exit_boot_services()")
introduced a regression on systems with large memory maps
causing them to hang on boot. The first "goto get_map" that was removed
from exit_boot insured there was enough room for the memory map when
efi_call_early(exit_boot_services) was called. This happens when
(nr_desc > ARRAY_SIZE(params->e820_table).

Chain of events:
   exit_boot()
     efi_exit_boot_services()
       efi_get_memory_map <- at this point the mm can't grow over 8 desc
       priv_func()
         exit_boot_func()
           allocate_e820ext() <- new mm grows over 8 desc from e820 alloc
       efi_call_early(exit_boot_services) <- mm key doesn't match so retry
       efi_call_early(get_memory_map) <- not enough room for new mm
       system hangs

This patch allocates the e820 buffer before calling efi_exit_boot_services
and fixes the regression.

Signed-off-by: Eric Snowberg <eric.snowberg@xxxxxxxxxx>
---
  arch/x86/boot/compressed/eboot.c | 63 +++++++++++++++++++++++++---------------
  1 file changed, 40 insertions(+), 23 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index cbf4b87..91a650e 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -866,11 +866,43 @@ static efi_status_t alloc_e820ext(u32 nr_desc, struct setup_data **e820ext,
         return status;
  }

+static efi_status_t allocate_e820(struct boot_params *params,
+                                 struct setup_data **e820ext,
+                                 u32 *e820ext_size)
+{
+       unsigned long map_size, desc_size, buff_size;
+       struct efi_boot_memmap boot_map;
+       efi_memory_desc_t *map;
+       efi_status_t status;
+       __u32 nr_desc;
+
+       boot_map.map =          &map;
+       boot_map.map_size =     &map_size;
+       boot_map.desc_size =    &desc_size;
+       boot_map.desc_ver =     NULL;
+       boot_map.key_ptr =      NULL;
+       boot_map.buff_size =    &buff_size;
+
+       status = efi_get_memory_map(sys_table, &boot_map);
+       if (status != EFI_SUCCESS)
+               return status;
+
+       nr_desc = buff_size / desc_size;
+

I think we should add some slack here. This function is now called
right before the first call to ExitBootServices(), which means events
may fire that allocate or free memory between this call to
efi_get_memory_map() and the final one which obtains the map key
(which is the whole reason for this complicated dance)

Slack in addition to what efi_get_memory_map() provides?



+       if (nr_desc > ARRAY_SIZE(params->e820_table)) {
+               u32 nr_e820ext = nr_desc - ARRAY_SIZE(params->e820_table);
+
+               status = alloc_e820ext(nr_e820ext, e820ext, e820ext_size);
+               if (status != EFI_SUCCESS)
+                       return status;
+       }
+
+       return EFI_SUCCESS;
+}
+
  struct exit_boot_struct {
         struct boot_params *boot_params;
         struct efi_info *efi;
-       struct setup_data *e820ext;
-       __u32 e820ext_size;
         bool is64;
  };

@@ -878,26 +910,11 @@ static efi_status_t exit_boot_func(efi_system_table_t *sys_table_arg,
                                    struct efi_boot_memmap *map,
                                    void *priv)
  {
-       static bool first = true;
         const char *signature;
         __u32 nr_desc;
         efi_status_t status;
         struct exit_boot_struct *p = priv;

-       if (first) {
-               nr_desc = *map->buff_size / *map->desc_size;
-               if (nr_desc > ARRAY_SIZE(p->boot_params->e820_table)) {
-                       u32 nr_e820ext = nr_desc -
-                                       ARRAY_SIZE(p->boot_params->e820_table);
-
-                       status = alloc_e820ext(nr_e820ext, &p->e820ext,
-                                              &p->e820ext_size);
-                       if (status != EFI_SUCCESS)
-                               return status;
-               }
-               first = false;
-       }
-
         signature = p->is64 ? EFI64_LOADER_SIGNATURE : EFI32_LOADER_SIGNATURE;
         memcpy(&p->efi->efi_loader_signature, signature, sizeof(__u32));

@@ -920,8 +937,8 @@ static efi_status_t exit_boot(struct boot_params *boot_params,
  {
         unsigned long map_sz, key, desc_size, buff_size;
         efi_memory_desc_t *mem_map;
-       struct setup_data *e820ext;
-       __u32 e820ext_size;
+       struct setup_data *e820ext = NULL;
+       __u32 e820ext_size = 0;
         efi_status_t status;
         __u32 desc_version;
         struct efi_boot_memmap map;
@@ -935,18 +952,18 @@ static efi_status_t exit_boot(struct boot_params *boot_params,
         map.buff_size =         &buff_size;
         priv.boot_params =      boot_params;
         priv.efi =              &boot_params->efi_info;
-       priv.e820ext =          NULL;
-       priv.e820ext_size =     0;
         priv.is64 =             is64;

+       status = allocate_e820(boot_params, &e820ext, &e820ext_size);
+       if (status != EFI_SUCCESS)
+               return status;
+
         /* Might as well exit boot services now */
         status = efi_exit_boot_services(sys_table, handle, &map, &priv,
                                         exit_boot_func);
         if (status != EFI_SUCCESS)
                 return status;

-       e820ext = priv.e820ext;
-       e820ext_size = priv.e820ext_size;
         /* Historic? */
         boot_params->alt_mem_k = 32 * 1024;

--
1.8.3.1



--
Jeffrey Hugo
Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.



[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux