pandora: defconfig: update
[pandora-kernel.git] / arch / x86 / platform / efi / efi.c
index 37718f0..e56da77 100644 (file)
 #define EFI_DEBUG      1
 #define PFX            "EFI: "
 
-int efi_enabled;
-EXPORT_SYMBOL(efi_enabled);
+#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 __read_mostly efi = {
        .mps        = EFI_INVALID_TABLE_ADDR,
@@ -70,9 +74,26 @@ struct efi_memory_map memmap;
 static struct efi efi_phys __initdata;
 static efi_system_table_t efi_systab __initdata;
 
+static inline bool efi_is_native(void)
+{
+       return IS_ENABLED(CONFIG_X86_64) == efi_enabled(EFI_64BIT);
+}
+
+unsigned long x86_efi_facility;
+
+/*
+ * Returns 1 if 'facility' is enabled, 0 otherwise.
+ */
+int efi_enabled(int facility)
+{
+       return test_bit(facility, &x86_efi_facility) != 0;
+}
+EXPORT_SYMBOL(efi_enabled);
+
+static bool disable_runtime = false;
 static int __init setup_noefi(char *arg)
 {
-       efi_enabled = 0;
+       disable_runtime = true;
        return 0;
 }
 early_param("noefi", setup_noefi);
@@ -87,6 +108,15 @@ 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)
 {
@@ -394,7 +424,7 @@ void __init efi_reserve_boot_services(void)
                 * - Not within any part of the kernel
                 * - Not the bios reserved area
                */
-               if ((start+size >= virt_to_phys(_text)
+               if ((start + size > virt_to_phys(_text)
                                && start <= virt_to_phys(_end)) ||
                        !e820_all_mapped(start, start+size, E820_RAM) ||
                        memblock_x86_check_reserved_size(&start, &size,
@@ -440,6 +470,9 @@ void __init efi_init(void)
        int i = 0;
        void *tmp;
 
+       if (!efi_is_native())
+               return;
+
 #ifdef CONFIG_X86_32
        efi_phys.systab = (efi_system_table_t *)boot_params.efi_info.efi_systab;
 #else
@@ -467,6 +500,8 @@ void __init efi_init(void)
                       efi.systab->hdr.revision >> 16,
                       efi.systab->hdr.revision & 0xffff);
 
+       set_bit(EFI_SYSTEM_TABLES, &x86_efi_facility);
+
        /*
         * Show what we know for posterity
         */
@@ -529,33 +564,39 @@ void __init efi_init(void)
        early_iounmap(config_tables,
                          efi.systab->nr_tables * sizeof(efi_config_table_t));
 
-       /*
-        * Check out the runtime services table. We need to map
-        * the runtime services table so that we can grab the physical
-        * address of several of the EFI runtime functions, needed to
-        * set the firmware into virtual mode.
-        */
-       runtime = early_ioremap((unsigned long)efi.systab->runtime,
-                               sizeof(efi_runtime_services_t));
-       if (runtime != NULL) {
-               /*
-                * We will only need *early* access to the following
-                * two EFI runtime services before set_virtual_address_map
-                * is invoked.
-                */
-               efi_phys.get_time = (efi_get_time_t *)runtime->get_time;
-               efi_phys.set_virtual_address_map =
-                       (efi_set_virtual_address_map_t *)
-                       runtime->set_virtual_address_map;
+       set_bit(EFI_CONFIG_TABLES, &x86_efi_facility);
+
+       if (!disable_runtime) {
                /*
-                * Make efi_get_time can be called before entering
-                * virtual mode.
+                * Check out the runtime services table. We need to map
+                * the runtime services table so that we can grab the physical
+                * address of several of the EFI runtime functions, needed to
+                * set the firmware into virtual mode.
                 */
-               efi.get_time = phys_efi_get_time;
-       } else
-               printk(KERN_ERR "Could not map the EFI runtime service "
-                      "table!\n");
-       early_iounmap(runtime, sizeof(efi_runtime_services_t));
+               runtime = early_ioremap((unsigned long)efi.systab->runtime,
+                                       sizeof(efi_runtime_services_t));
+               if (runtime != NULL) {
+                       /*
+                        * We will only need *early* access to the following
+                        * two EFI runtime services before set_virtual_address_map
+                        * is invoked.
+                        */
+                       efi_phys.get_time = (efi_get_time_t *)runtime->get_time;
+                       efi_phys.set_virtual_address_map =
+                               (efi_set_virtual_address_map_t *)
+                               runtime->set_virtual_address_map;
+                       /*
+                        * Make efi_get_time can be called before entering
+                        * virtual mode.
+                        */
+                       efi.get_time = phys_efi_get_time;
+                       
+                       set_bit(EFI_RUNTIME_SERVICES, &x86_efi_facility);
+               } else
+                       printk(KERN_ERR "Could not map the EFI runtime service "
+                              "table!\n");
+               early_iounmap(runtime, sizeof(efi_runtime_services_t));
+       }
 
        /* Map the EFI memory map */
        memmap.map = early_ioremap((unsigned long)memmap.phys_map,
@@ -571,10 +612,7 @@ void __init efi_init(void)
        if (add_efi_memmap)
                do_add_efi_memmap();
 
-#ifdef CONFIG_X86_32
-       x86_platform.get_wallclock = efi_get_time;
-       x86_platform.set_wallclock = efi_set_rtc_mmss;
-#endif
+       set_bit(EFI_MEMMAP, &x86_efi_facility);
 
 #if EFI_DEBUG
        print_efi_memmap();
@@ -660,10 +698,13 @@ void __init efi_enter_virtual_mode(void)
 
        for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
                md = p;
-               if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
-                   md->type != EFI_BOOT_SERVICES_CODE &&
-                   md->type != EFI_BOOT_SERVICES_DATA)
-                       continue;
+               if (!(md->attribute & EFI_MEMORY_RUNTIME)) {
+#ifdef CONFIG_X86_64
+                       if (md->type != EFI_BOOT_SERVICES_CODE &&
+                           md->type != EFI_BOOT_SERVICES_DATA)
+#endif
+                               continue;
+               }
 
                size = md->num_pages << EFI_PAGE_SHIFT;
                end = md->phys_addr + size;
@@ -731,6 +772,7 @@ void __init efi_enter_virtual_mode(void)
         *
         * Call EFI services through wrapper functions.
         */
+       efi.runtime_version = efi_systab.hdr.revision;
        efi.get_time = virt_efi_get_time;
        efi.set_time = virt_efi_set_time;
        efi.get_wakeup_time = virt_efi_get_wakeup_time;
@@ -746,9 +788,17 @@ void __init efi_enter_virtual_mode(void)
        efi.query_capsule_caps = virt_efi_query_capsule_caps;
        if (__supported_pte_mask & _PAGE_NX)
                runtime_code_page_mkexec();
+       clear_bit(EFI_MEMMAP, &x86_efi_facility);
        early_iounmap(memmap.map, memmap.nr_map * memmap.desc_size);
        memmap.map = NULL;
        kfree(new_memmap);
+
+       /* 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);
 }
 
 /*
@@ -759,6 +809,9 @@ u32 efi_mem_type(unsigned long phys_addr)
        efi_memory_desc_t *md;
        void *p;
 
+       if (!efi_enabled(EFI_MEMMAP))
+               return 0;
+
        for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
                md = p;
                if ((md->phys_addr <= phys_addr) &&
@@ -783,3 +836,85 @@ u64 efi_mem_attributes(unsigned long phys_addr)
        }
        return 0;
 }
+
+/*
+ * Some firmware has serious problems when using more than 50% of the EFI
+ * variable store, i.e. it triggers bugs that can brick machines. Ensure that
+ * we never use more than this 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;
+
+       /*
+        * Some firmware implementations refuse to boot if there's insufficient
+        * space in the variable store. 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);