x86/efi: Move all workarounds to a separate file quirks.c
authorSaurabh Tangri <saurabh.tangri@intel.com>
Mon, 2 Jun 2014 12:18:35 +0000 (05:18 -0700)
committerMatt Fleming <matt.fleming@intel.com>
Thu, 19 Jun 2014 10:14:33 +0000 (11:14 +0100)
Currently, it's difficult to find all the workarounds that are
applied when running on EFI, because they're littered throughout
various code paths. This change moves all of them into a separate
file with the hope that it will be come the single location for all
our well documented quirks.

Signed-off-by: Saurabh Tangri <saurabh.tangri@intel.com>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
arch/x86/include/asm/efi.h
arch/x86/platform/efi/Makefile
arch/x86/platform/efi/efi.c
arch/x86/platform/efi/quirks.c [new file with mode: 0644]

index 1eb5f64..3dbf56e 100644 (file)
@@ -104,6 +104,8 @@ extern void __init runtime_code_page_mkexec(void);
 extern void __init efi_runtime_mkexec(void);
 extern void __init efi_dump_pagetable(void);
 extern void __init efi_apply_memmap_quirks(void);
+extern int __init efi_reuse_config(u64 tables, int nr_tables);
+extern void efi_delete_dummy_variable(void);
 
 struct efi_setup_data {
        u64 fw_vendor;
index d51045a..2846aaa 100644 (file)
@@ -1,4 +1,4 @@
-obj-$(CONFIG_EFI)              += efi.o efi_$(BITS).o efi_stub_$(BITS).o
+obj-$(CONFIG_EFI)              += quirks.o efi.o efi_$(BITS).o efi_stub_$(BITS).o
 obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o
 obj-$(CONFIG_EARLY_PRINTK_EFI) += early_printk.o
 obj-$(CONFIG_EFI_MIXED)                += efi_thunk_$(BITS).o
index 87fc96b..f852443 100644 (file)
 
 #define EFI_DEBUG
 
-#define EFI_MIN_RESERVE 5120
-
-#define EFI_DUMMY_GUID \
-       EFI_GUID(0x4424ac57, 0xbe4b, 0x47dd, 0x9e, 0x97, 0xed, 0x50, 0xf0, 0x9f, 0x92, 0xa9)
-
-static efi_char16_t efi_dummy_name[6] = { 'D', 'U', 'M', 'M', 'Y', 0 };
-
 struct efi_memory_map memmap;
 
 static struct efi efi_phys __initdata;
@@ -95,15 +88,6 @@ static int __init setup_add_efi_memmap(char *arg)
 }
 early_param("add_efi_memmap", setup_add_efi_memmap);
 
-static bool efi_no_storage_paranoia;
-
-static int __init setup_storage_paranoia(char *arg)
-{
-       efi_no_storage_paranoia = true;
-       return 0;
-}
-early_param("efi_no_storage_paranoia", setup_storage_paranoia);
-
 static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc)
 {
        unsigned long flags;
@@ -392,37 +376,6 @@ static void __init print_efi_memmap(void)
 #endif  /*  EFI_DEBUG  */
 }
 
-void __init efi_reserve_boot_services(void)
-{
-       void *p;
-
-       for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
-               efi_memory_desc_t *md = p;
-               u64 start = md->phys_addr;
-               u64 size = md->num_pages << EFI_PAGE_SHIFT;
-
-               if (md->type != EFI_BOOT_SERVICES_CODE &&
-                   md->type != EFI_BOOT_SERVICES_DATA)
-                       continue;
-               /* Only reserve where possible:
-                * - Not within any already allocated areas
-                * - Not over any memory area (really needed, if above?)
-                * - Not within any part of the kernel
-                * - Not the bios reserved area
-               */
-               if ((start + size > __pa_symbol(_text)
-                               && start <= __pa_symbol(_end)) ||
-                       !e820_all_mapped(start, start+size, E820_RAM) ||
-                       memblock_is_region_reserved(start, size)) {
-                       /* Could not reserve, skip it */
-                       md->num_pages = 0;
-                       memblock_dbg("Could not reserve boot range [0x%010llx-0x%010llx]\n",
-                                    start, start+size-1);
-               } else
-                       memblock_reserve(start, size);
-       }
-}
-
 void __init efi_unmap_memmap(void)
 {
        clear_bit(EFI_MEMMAP, &efi.flags);
@@ -432,29 +385,6 @@ void __init efi_unmap_memmap(void)
        }
 }
 
-void __init efi_free_boot_services(void)
-{
-       void *p;
-
-       for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
-               efi_memory_desc_t *md = p;
-               unsigned long long start = md->phys_addr;
-               unsigned long long size = md->num_pages << EFI_PAGE_SHIFT;
-
-               if (md->type != EFI_BOOT_SERVICES_CODE &&
-                   md->type != EFI_BOOT_SERVICES_DATA)
-                       continue;
-
-               /* Could not reserve boot area */
-               if (!size)
-                       continue;
-
-               free_bootmem_late(start, size);
-       }
-
-       efi_unmap_memmap();
-}
-
 static int __init efi_systab_init(void *phys)
 {
        if (efi_enabled(EFI_64BIT)) {
@@ -649,62 +579,6 @@ static int __init efi_memmap_init(void)
        return 0;
 }
 
-/*
- * A number of config table entries get remapped to virtual addresses
- * after entering EFI virtual mode. However, the kexec kernel requires
- * their physical addresses therefore we pass them via setup_data and
- * correct those entries to their respective physical addresses here.
- *
- * Currently only handles smbios which is necessary for some firmware
- * implementation.
- */
-static int __init efi_reuse_config(u64 tables, int nr_tables)
-{
-       int i, sz, ret = 0;
-       void *p, *tablep;
-       struct efi_setup_data *data;
-
-       if (!efi_setup)
-               return 0;
-
-       if (!efi_enabled(EFI_64BIT))
-               return 0;
-
-       data = early_memremap(efi_setup, sizeof(*data));
-       if (!data) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       if (!data->smbios)
-               goto out_memremap;
-
-       sz = sizeof(efi_config_table_64_t);
-
-       p = tablep = early_memremap(tables, nr_tables * sz);
-       if (!p) {
-               pr_err("Could not map Configuration table!\n");
-               ret = -ENOMEM;
-               goto out_memremap;
-       }
-
-       for (i = 0; i < efi.systab->nr_tables; i++) {
-               efi_guid_t guid;
-
-               guid = ((efi_config_table_64_t *)p)->guid;
-
-               if (!efi_guidcmp(guid, SMBIOS_TABLE_GUID))
-                       ((efi_config_table_64_t *)p)->table = data->smbios;
-               p += sz;
-       }
-       early_iounmap(tablep, nr_tables * sz);
-
-out_memremap:
-       early_iounmap(data, sizeof(*data));
-out:
-       return ret;
-}
-
 void __init efi_init(void)
 {
        efi_char16_t *c16;
@@ -1057,11 +931,7 @@ static void __init kexec_enter_virtual_mode(void)
                runtime_code_page_mkexec();
 
        /* clean DUMMY object */
-       efi.set_variable(efi_dummy_name, &EFI_DUMMY_GUID,
-                        EFI_VARIABLE_NON_VOLATILE |
-                        EFI_VARIABLE_BOOTSERVICE_ACCESS |
-                        EFI_VARIABLE_RUNTIME_ACCESS,
-                        0, NULL);
+       efi_delete_dummy_variable();
 #endif
 }
 
@@ -1179,11 +1049,7 @@ static void __init __efi_enter_virtual_mode(void)
        free_pages((unsigned long)new_memmap, pg_shift);
 
        /* clean DUMMY object */
-       efi.set_variable(efi_dummy_name, &EFI_DUMMY_GUID,
-                        EFI_VARIABLE_NON_VOLATILE |
-                        EFI_VARIABLE_BOOTSERVICE_ACCESS |
-                        EFI_VARIABLE_RUNTIME_ACCESS,
-                        0, NULL);
+       efi_delete_dummy_variable();
 }
 
 void __init efi_enter_virtual_mode(void)
@@ -1230,86 +1096,6 @@ u64 efi_mem_attributes(unsigned long phys_addr)
        return 0;
 }
 
-/*
- * Some firmware implementations refuse to boot if there's insufficient space
- * in the variable store. Ensure that we never use more than a safe limit.
- *
- * Return EFI_SUCCESS if it is safe to write 'size' bytes to the variable
- * store.
- */
-efi_status_t efi_query_variable_store(u32 attributes, unsigned long size)
-{
-       efi_status_t status;
-       u64 storage_size, remaining_size, max_size;
-
-       if (!(attributes & EFI_VARIABLE_NON_VOLATILE))
-               return 0;
-
-       status = efi.query_variable_info(attributes, &storage_size,
-                                        &remaining_size, &max_size);
-       if (status != EFI_SUCCESS)
-               return status;
-
-       /*
-        * We account for that by refusing the write if permitting it would
-        * reduce the available space to under 5KB. This figure was provided by
-        * Samsung, so should be safe.
-        */
-       if ((remaining_size - size < EFI_MIN_RESERVE) &&
-               !efi_no_storage_paranoia) {
-
-               /*
-                * Triggering garbage collection may require that the firmware
-                * generate a real EFI_OUT_OF_RESOURCES error. We can force
-                * that by attempting to use more space than is available.
-                */
-               unsigned long dummy_size = remaining_size + 1024;
-               void *dummy = kzalloc(dummy_size, GFP_ATOMIC);
-
-               if (!dummy)
-                       return EFI_OUT_OF_RESOURCES;
-
-               status = efi.set_variable(efi_dummy_name, &EFI_DUMMY_GUID,
-                                         EFI_VARIABLE_NON_VOLATILE |
-                                         EFI_VARIABLE_BOOTSERVICE_ACCESS |
-                                         EFI_VARIABLE_RUNTIME_ACCESS,
-                                         dummy_size, dummy);
-
-               if (status == EFI_SUCCESS) {
-                       /*
-                        * This should have failed, so if it didn't make sure
-                        * that we delete it...
-                        */
-                       efi.set_variable(efi_dummy_name, &EFI_DUMMY_GUID,
-                                        EFI_VARIABLE_NON_VOLATILE |
-                                        EFI_VARIABLE_BOOTSERVICE_ACCESS |
-                                        EFI_VARIABLE_RUNTIME_ACCESS,
-                                        0, dummy);
-               }
-
-               kfree(dummy);
-
-               /*
-                * The runtime code may now have triggered a garbage collection
-                * run, so check the variable info again
-                */
-               status = efi.query_variable_info(attributes, &storage_size,
-                                                &remaining_size, &max_size);
-
-               if (status != EFI_SUCCESS)
-                       return status;
-
-               /*
-                * There still isn't enough room, so return an error
-                */
-               if (remaining_size - size < EFI_MIN_RESERVE)
-                       return EFI_OUT_OF_RESOURCES;
-       }
-
-       return EFI_SUCCESS;
-}
-EXPORT_SYMBOL_GPL(efi_query_variable_store);
-
 static int __init parse_efi_cmdline(char *str)
 {
        if (*str == '=')
@@ -1321,22 +1107,3 @@ static int __init parse_efi_cmdline(char *str)
        return 0;
 }
 early_param("efi", parse_efi_cmdline);
-
-void __init efi_apply_memmap_quirks(void)
-{
-       /*
-        * Once setup is done earlier, unmap the EFI memory map on mismatched
-        * firmware/kernel architectures since there is no support for runtime
-        * services.
-        */
-       if (!efi_runtime_supported()) {
-               pr_info("efi: Setup done, disabling due to 32/64-bit mismatch\n");
-               efi_unmap_memmap();
-       }
-
-       /*
-        * UV doesn't support the new EFI pagetable mapping yet.
-        */
-       if (is_uv_system())
-               set_bit(EFI_OLD_MEMMAP, &efi.flags);
-}
diff --git a/arch/x86/platform/efi/quirks.c b/arch/x86/platform/efi/quirks.c
new file mode 100644 (file)
index 0000000..7e3099c
--- /dev/null
@@ -0,0 +1,267 @@
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/efi.h>
+#include <linux/slab.h>
+#include <linux/memblock.h>
+#include <linux/bootmem.h>
+#include <asm/efi.h>
+#include <asm/uv/uv.h>
+
+#define EFI_MIN_RESERVE 5120
+
+#define EFI_DUMMY_GUID \
+       EFI_GUID(0x4424ac57, 0xbe4b, 0x47dd, 0x9e, 0x97, 0xed, 0x50, 0xf0, 0x9f, 0x92, 0xa9)
+
+static efi_char16_t efi_dummy_name[6] = { 'D', 'U', 'M', 'M', 'Y', 0 };
+
+static bool efi_no_storage_paranoia;
+
+/*
+ * Some firmware implementations refuse to boot if there's insufficient
+ * space in the variable store. The implementation of garbage collection
+ * in some FW versions causes stale (deleted) variables to take up space
+ * longer than intended and space is only freed once the store becomes
+ * almost completely full.
+ *
+ * Enabling this option disables the space checks in
+ * efi_query_variable_store() and forces garbage collection.
+ *
+ * Only enable this option if deleting EFI variables does not free up
+ * space in your variable store, e.g. if despite deleting variables
+ * you're unable to create new ones.
+ */
+static int __init setup_storage_paranoia(char *arg)
+{
+       efi_no_storage_paranoia = true;
+       return 0;
+}
+early_param("efi_no_storage_paranoia", setup_storage_paranoia);
+
+/*
+ * Deleting the dummy variable which kicks off garbage collection
+*/
+void efi_delete_dummy_variable(void)
+{
+       efi.set_variable(efi_dummy_name, &EFI_DUMMY_GUID,
+                        EFI_VARIABLE_NON_VOLATILE |
+                        EFI_VARIABLE_BOOTSERVICE_ACCESS |
+                        EFI_VARIABLE_RUNTIME_ACCESS,
+                        0, NULL);
+}
+
+/*
+ * Some firmware implementations refuse to boot if there's insufficient space
+ * in the variable store. Ensure that we never use more than a safe limit.
+ *
+ * Return EFI_SUCCESS if it is safe to write 'size' bytes to the variable
+ * store.
+ */
+efi_status_t efi_query_variable_store(u32 attributes, unsigned long size)
+{
+       efi_status_t status;
+       u64 storage_size, remaining_size, max_size;
+
+       if (!(attributes & EFI_VARIABLE_NON_VOLATILE))
+               return 0;
+
+       status = efi.query_variable_info(attributes, &storage_size,
+                                        &remaining_size, &max_size);
+       if (status != EFI_SUCCESS)
+               return status;
+
+       /*
+        * We account for that by refusing the write if permitting it would
+        * reduce the available space to under 5KB. This figure was provided by
+        * Samsung, so should be safe.
+        */
+       if ((remaining_size - size < EFI_MIN_RESERVE) &&
+               !efi_no_storage_paranoia) {
+
+               /*
+                * Triggering garbage collection may require that the firmware
+                * generate a real EFI_OUT_OF_RESOURCES error. We can force
+                * that by attempting to use more space than is available.
+                */
+               unsigned long dummy_size = remaining_size + 1024;
+               void *dummy = kzalloc(dummy_size, GFP_ATOMIC);
+
+               if (!dummy)
+                       return EFI_OUT_OF_RESOURCES;
+
+               status = efi.set_variable(efi_dummy_name, &EFI_DUMMY_GUID,
+                                         EFI_VARIABLE_NON_VOLATILE |
+                                         EFI_VARIABLE_BOOTSERVICE_ACCESS |
+                                         EFI_VARIABLE_RUNTIME_ACCESS,
+                                         dummy_size, dummy);
+
+               if (status == EFI_SUCCESS) {
+                       /*
+                        * This should have failed, so if it didn't make sure
+                        * that we delete it...
+                        */
+                       efi_delete_dummy_variable();
+               }
+
+               kfree(dummy);
+
+               /*
+                * The runtime code may now have triggered a garbage collection
+                * run, so check the variable info again
+                */
+               status = efi.query_variable_info(attributes, &storage_size,
+                                                &remaining_size, &max_size);
+
+               if (status != EFI_SUCCESS)
+                       return status;
+
+               /*
+                * There still isn't enough room, so return an error
+                */
+               if (remaining_size - size < EFI_MIN_RESERVE)
+                       return EFI_OUT_OF_RESOURCES;
+       }
+
+       return EFI_SUCCESS;
+}
+EXPORT_SYMBOL_GPL(efi_query_variable_store);
+
+/*
+ * The UEFI specification makes it clear that the operating system is free to do
+ * whatever it wants with boot services code after ExitBootServices() has been
+ * called. Ignoring this recommendation a significant bunch of EFI implementations 
+ * continue calling into boot services code (SetVirtualAddressMap). In order to 
+ * work around such buggy implementations we reserve boot services region during 
+ * EFI init and make sure it stays executable. Then, after SetVirtualAddressMap(), it
+* is discarded.
+*/
+void __init efi_reserve_boot_services(void)
+{
+       void *p;
+
+       for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
+               efi_memory_desc_t *md = p;
+               u64 start = md->phys_addr;
+               u64 size = md->num_pages << EFI_PAGE_SHIFT;
+
+               if (md->type != EFI_BOOT_SERVICES_CODE &&
+                   md->type != EFI_BOOT_SERVICES_DATA)
+                       continue;
+               /* Only reserve where possible:
+                * - Not within any already allocated areas
+                * - Not over any memory area (really needed, if above?)
+                * - Not within any part of the kernel
+                * - Not the bios reserved area
+               */
+               if ((start + size > __pa_symbol(_text)
+                               && start <= __pa_symbol(_end)) ||
+                       !e820_all_mapped(start, start+size, E820_RAM) ||
+                       memblock_is_region_reserved(start, size)) {
+                       /* Could not reserve, skip it */
+                       md->num_pages = 0;
+                       memblock_dbg("Could not reserve boot range [0x%010llx-0x%010llx]\n",
+                                    start, start+size-1);
+               } else
+                       memblock_reserve(start, size);
+       }
+}
+
+void __init efi_free_boot_services(void)
+{
+       void *p;
+
+       for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
+               efi_memory_desc_t *md = p;
+               unsigned long long start = md->phys_addr;
+               unsigned long long size = md->num_pages << EFI_PAGE_SHIFT;
+
+               if (md->type != EFI_BOOT_SERVICES_CODE &&
+                   md->type != EFI_BOOT_SERVICES_DATA)
+                       continue;
+
+               /* Could not reserve boot area */
+               if (!size)
+                       continue;
+
+               free_bootmem_late(start, size);
+       }
+
+       efi_unmap_memmap();
+}
+
+/*
+ * A number of config table entries get remapped to virtual addresses
+ * after entering EFI virtual mode. However, the kexec kernel requires
+ * their physical addresses therefore we pass them via setup_data and
+ * correct those entries to their respective physical addresses here.
+ *
+ * Currently only handles smbios which is necessary for some firmware
+ * implementation.
+ */
+int __init efi_reuse_config(u64 tables, int nr_tables)
+{
+       int i, sz, ret = 0;
+       void *p, *tablep;
+       struct efi_setup_data *data;
+
+       if (!efi_setup)
+               return 0;
+
+       if (!efi_enabled(EFI_64BIT))
+               return 0;
+
+       data = early_memremap(efi_setup, sizeof(*data));
+       if (!data) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       if (!data->smbios)
+               goto out_memremap;
+
+       sz = sizeof(efi_config_table_64_t);
+
+       p = tablep = early_memremap(tables, nr_tables * sz);
+       if (!p) {
+               pr_err("Could not map Configuration table!\n");
+               ret = -ENOMEM;
+               goto out_memremap;
+       }
+
+       for (i = 0; i < efi.systab->nr_tables; i++) {
+               efi_guid_t guid;
+
+               guid = ((efi_config_table_64_t *)p)->guid;
+
+               if (!efi_guidcmp(guid, SMBIOS_TABLE_GUID))
+                       ((efi_config_table_64_t *)p)->table = data->smbios;
+               p += sz;
+       }
+       early_iounmap(tablep, nr_tables * sz);
+
+out_memremap:
+       early_iounmap(data, sizeof(*data));
+out:
+       return ret;
+}
+
+void __init efi_apply_memmap_quirks(void)
+{
+       /*
+        * Once setup is done earlier, unmap the EFI memory map on mismatched
+        * firmware/kernel architectures since there is no support for runtime
+        * services.
+        */
+       if (!efi_runtime_supported()) {
+               pr_info("efi: Setup done, disabling due to 32/64-bit mismatch\n");
+               efi_unmap_memmap();
+       }
+
+       /*
+        * UV doesn't support the new EFI pagetable mapping yet.
+        */
+       if (is_uv_system())
+               set_bit(EFI_OLD_MEMMAP, &efi.flags);
+}