efi: Make 'efi_enabled' a function to query EFI facilities
authorMatt Fleming <matt.fleming@intel.com>
Wed, 14 Nov 2012 09:42:35 +0000 (09:42 +0000)
committerBen Hutchings <ben@decadent.org.uk>
Wed, 6 Feb 2013 04:33:50 +0000 (04:33 +0000)
commit 83e68189745ad931c2afd45d8ee3303929233e7f upstream.

Originally 'efi_enabled' indicated whether a kernel was booted from
EFI firmware. Over time its semantics have changed, and it now
indicates whether or not we are booted on an EFI machine with
bit-native firmware, e.g. 64-bit kernel with 64-bit firmware.

The immediate motivation for this patch is the bug report at,

    https://bugs.launchpad.net/ubuntu-cdimage/+bug/1040557

which details how running a platform driver on an EFI machine that is
designed to run under BIOS can cause the machine to become
bricked. Also, the following report,

    https://bugzilla.kernel.org/show_bug.cgi?id=47121

details how running said driver can also cause Machine Check
Exceptions. Drivers need a new means of detecting whether they're
running on an EFI machine, as sadly the expression,

    if (!efi_enabled)

hasn't been a sufficient condition for quite some time.

Users actually want to query 'efi_enabled' for different reasons -
what they really want access to is the list of available EFI
facilities.

For instance, the x86 reboot code needs to know whether it can invoke
the ResetSystem() function provided by the EFI runtime services, while
the ACPI OSL code wants to know whether the EFI config tables were
mapped successfully. There are also checks in some of the platform
driver code to simply see if they're running on an EFI machine (which
would make it a bad idea to do BIOS-y things).

This patch is a prereq for the samsung-laptop fix patch.

Cc: David Airlie <airlied@linux.ie>
Cc: Corentin Chary <corentincj@iksaif.net>
Cc: Matthew Garrett <mjg59@srcf.ucam.org>
Cc: Dave Jiang <dave.jiang@intel.com>
Cc: Olof Johansson <olof@lixom.net>
Cc: Peter Jones <pjones@redhat.com>
Cc: Colin Ian King <colin.king@canonical.com>
Cc: Steve Langasek <steve.langasek@canonical.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Konrad Rzeszutek Wilk <konrad@kernel.org>
Cc: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
[bwh: Backported to 3.2:
 - Adjust context (a lot)
 - Add efi_is_native() function from commit 5189c2a7c776
   ('x86: efi: Turn off efi_enabled after setup on mixed fw/kernel')
 - Make efi_init() bail out when booted non-native, as it would previously
   not be called in this case
 - Drop inapplicable changes to start_kernel()]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
13 files changed:
arch/x86/include/asm/efi.h
arch/x86/kernel/reboot.c
arch/x86/kernel/setup.c
arch/x86/platform/efi/efi.c
drivers/acpi/osl.c
drivers/firmware/dmi_scan.c
drivers/firmware/efivars.c
drivers/firmware/iscsi_ibft_find.c
drivers/gpu/drm/radeon/radeon_device.c
drivers/platform/x86/ibm_rtl.c
drivers/scsi/isci/init.c
include/linux/efi.h
init/main.c

index 7093e4a6a0bc6dd5644b2b14cbef9ef5f4085eef..035cd819ed416e5325b93b73cb909cfb8b2a84b4 100644 (file)
@@ -90,6 +90,7 @@ extern void __iomem *efi_ioremap(unsigned long addr, unsigned long size,
 #endif /* CONFIG_X86_32 */
 
 extern int add_efi_memmap;
+extern unsigned long x86_efi_facility;
 extern void efi_set_executable(efi_memory_desc_t *md, bool executable);
 extern void efi_memblock_x86_reserve_range(void);
 extern void efi_call_phys_prelog(void);
index e61f79c97f13aca4d284b90bbb43cadb0cd0a32e..47f4e5ff55ac7ef8bacb9b06aaf7c446dfb545f3 100644 (file)
@@ -603,7 +603,7 @@ static void native_machine_emergency_restart(void)
                        break;
 
                case BOOT_EFI:
-                       if (efi_enabled)
+                       if (efi_enabled(EFI_RUNTIME_SERVICES))
                                efi.reset_system(reboot_mode ?
                                                 EFI_RESET_WARM :
                                                 EFI_RESET_COLD,
index 0d403aaf0b35f3ac2fbc1c0faa7b371dc92bd301..58d7323772552efaa8d7adc2922670a424119b90 100644 (file)
@@ -750,15 +750,16 @@ void __init setup_arch(char **cmdline_p)
 #endif
 #ifdef CONFIG_EFI
        if (!strncmp((char *)&boot_params.efi_info.efi_loader_signature,
-#ifdef CONFIG_X86_32
-                    "EL32",
-#else
-                    "EL64",
-#endif
-        4)) {
-               efi_enabled = 1;
-               efi_memblock_x86_reserve_range();
+                    "EL32", 4)) {
+               set_bit(EFI_BOOT, &x86_efi_facility);
+       } else if (!strncmp((char *)&boot_params.efi_info.efi_loader_signature,
+                    "EL64", 4)) {
+               set_bit(EFI_BOOT, &x86_efi_facility);
+               set_bit(EFI_64BIT, &x86_efi_facility);
        }
+
+       if (efi_enabled(EFI_BOOT))
+               efi_memblock_x86_reserve_range();
 #endif
 
        x86_init.oem.arch_setup();
@@ -831,7 +832,7 @@ void __init setup_arch(char **cmdline_p)
 
        finish_e820_parsing();
 
-       if (efi_enabled)
+       if (efi_enabled(EFI_BOOT))
                efi_init();
 
        dmi_scan_machine();
@@ -914,7 +915,7 @@ void __init setup_arch(char **cmdline_p)
         * The EFI specification says that boot service code won't be called
         * after ExitBootServices(). This is, in fact, a lie.
         */
-       if (efi_enabled)
+       if (efi_enabled(EFI_MEMMAP))
                efi_reserve_boot_services();
 
        /* preallocate 4k for mptable mpc */
@@ -1048,7 +1049,7 @@ void __init setup_arch(char **cmdline_p)
 
 #ifdef CONFIG_VT
 #if defined(CONFIG_VGA_CONSOLE)
-       if (!efi_enabled || (efi_mem_type(0xa0000) != EFI_CONVENTIONAL_MEMORY))
+       if (!efi_enabled(EFI_BOOT) || (efi_mem_type(0xa0000) != EFI_CONVENTIONAL_MEMORY))
                conswitchp = &vga_con;
 #elif defined(CONFIG_DUMMY_CONSOLE)
        conswitchp = &dummy_con;
index 4d320b290ce0327bb9f9b83df9876319d7932cf4..ace36945f568585b3a3cc952698fa1dc5fe30199 100644 (file)
@@ -49,9 +49,6 @@
 #define EFI_DEBUG      1
 #define PFX            "EFI: "
 
-int efi_enabled;
-EXPORT_SYMBOL(efi_enabled);
-
 struct efi __read_mostly efi = {
        .mps        = EFI_INVALID_TABLE_ADDR,
        .acpi       = EFI_INVALID_TABLE_ADDR,
@@ -70,9 +67,25 @@ 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 int __init setup_noefi(char *arg)
 {
-       efi_enabled = 0;
+       clear_bit(EFI_BOOT, &x86_efi_facility);
        return 0;
 }
 early_param("noefi", setup_noefi);
@@ -440,6 +453,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 +483,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,6 +547,8 @@ void __init efi_init(void)
        early_iounmap(config_tables,
                          efi.systab->nr_tables * sizeof(efi_config_table_t));
 
+       set_bit(EFI_CONFIG_TABLES, &x86_efi_facility);
+
        /*
         * Check out the runtime services table. We need to map
         * the runtime services table so that we can grab the physical
@@ -552,6 +572,8 @@ void __init efi_init(void)
                 * 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");
@@ -571,6 +593,8 @@ void __init efi_init(void)
        if (add_efi_memmap)
                do_add_efi_memmap();
 
+       set_bit(EFI_MEMMAP, &x86_efi_facility);
+
 #ifdef CONFIG_X86_32
        x86_platform.get_wallclock = efi_get_time;
        x86_platform.set_wallclock = efi_set_rtc_mmss;
@@ -747,6 +771,7 @@ 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);
@@ -760,6 +785,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) &&
index f31c5c5f1b7e083351e013649f1e1d73fdd74fa6..a6664d21bc7a9e4f5e2e7a34742a937841312bba 100644 (file)
@@ -255,7 +255,7 @@ acpi_physical_address __init acpi_os_get_root_pointer(void)
                return acpi_rsdp;
 #endif
 
-       if (efi_enabled) {
+       if (efi_enabled(EFI_CONFIG_TABLES)) {
                if (efi.acpi20 != EFI_INVALID_TABLE_ADDR)
                        return efi.acpi20;
                else if (efi.acpi != EFI_INVALID_TABLE_ADDR)
index b298158cb9224dd24eb65c5c00d2733b5d6cfc98..2d1539f2f7dddb5301fca5ff3ce2b239ef48f39b 100644 (file)
@@ -432,7 +432,7 @@ void __init dmi_scan_machine(void)
        char __iomem *p, *q;
        int rc;
 
-       if (efi_enabled) {
+       if (efi_enabled(EFI_CONFIG_TABLES)) {
                if (efi.smbios == EFI_INVALID_TABLE_ADDR)
                        goto error;
 
index 3e60e8d9f03d18f17cba8daec42c41f001a0372d..5d5a86843773e31da94cf6b5ceb0d57827b157f1 100644 (file)
@@ -1222,7 +1222,7 @@ efivars_init(void)
        printk(KERN_INFO "EFI Variables Facility v%s %s\n", EFIVARS_VERSION,
               EFIVARS_DATE);
 
-       if (!efi_enabled)
+       if (!efi_enabled(EFI_RUNTIME_SERVICES))
                return 0;
 
        /* For now we'll register the efi directory at /sys/firmware/efi */
@@ -1260,7 +1260,7 @@ err_put:
 static void __exit
 efivars_exit(void)
 {
-       if (efi_enabled) {
+       if (efi_enabled(EFI_RUNTIME_SERVICES)) {
                unregister_efivars(&__efivars);
                kobject_put(efi_kobj);
        }
index 4da4eb9ae92604c35349ebaeb39b4a612bac3ed6..2224f1dc074b1329d7ce9c80b78bef4fffc6e98b 100644 (file)
@@ -99,7 +99,7 @@ unsigned long __init find_ibft_region(unsigned long *sizep)
        /* iBFT 1.03 section 1.4.3.1 mandates that UEFI machines will
         * only use ACPI for this */
 
-       if (!efi_enabled)
+       if (!efi_enabled(EFI_BOOT))
                find_ibft_in_mem();
 
        if (ibft_addr) {
index c5762e3286a21b87e8009731c76e850daf778346..bd959c193cdd57c27e760e6c978ee906546fdb09 100644 (file)
@@ -354,7 +354,8 @@ bool radeon_card_posted(struct radeon_device *rdev)
 {
        uint32_t reg;
 
-       if (efi_enabled && rdev->pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE)
+       if (efi_enabled(EFI_BOOT) &&
+           rdev->pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE)
                return false;
 
        /* first check CRTCs */
index 811d436cd677a0c1bab06cc7caa1e3d7ce317d67..27043866e9743af417c545768437531afee6db8f 100644 (file)
@@ -255,7 +255,7 @@ static int __init ibm_rtl_init(void) {
        if (force)
                pr_warn("module loaded by force\n");
        /* first ensure that we are running on IBM HW */
-       else if (efi_enabled || !dmi_check_system(ibm_rtl_dmi_table))
+       else if (efi_enabled(EFI_BOOT) || !dmi_check_system(ibm_rtl_dmi_table))
                return -ENODEV;
 
        /* Get the address for the Extended BIOS Data Area */
index 5c8b0dc5c0c865ab71bac9f5a5fa50085211b0c1..378438828a2eb081412003797f1e489bedfae0a7 100644 (file)
@@ -459,7 +459,7 @@ static int __devinit isci_pci_probe(struct pci_dev *pdev, const struct pci_devic
                return -ENOMEM;
        pci_set_drvdata(pdev, pci_info);
 
-       if (efi_enabled)
+       if (efi_enabled(EFI_RUNTIME_SERVICES))
                orom = isci_get_efi_var(pdev);
 
        if (!orom)
index 1328d8c0006e3262f874c67bdf11d8cc22729e5f..1721c419c0e700beaf6f522e1ccbdd37374a4383 100644 (file)
@@ -364,17 +364,30 @@ extern int __init efi_setup_pcdp_console(char *);
 #endif
 
 /*
- * We play games with efi_enabled so that the compiler will, if possible, remove
- * EFI-related code altogether.
+ * We play games with efi_enabled so that the compiler will, if
+ * possible, remove EFI-related code altogether.
  */
+#define EFI_BOOT               0       /* Were we booted from EFI? */
+#define EFI_SYSTEM_TABLES      1       /* Can we use EFI system tables? */
+#define EFI_CONFIG_TABLES      2       /* Can we use EFI config tables? */
+#define EFI_RUNTIME_SERVICES   3       /* Can we use runtime services? */
+#define EFI_MEMMAP             4       /* Can we use EFI memory map? */
+#define EFI_64BIT              5       /* Is the firmware 64-bit? */
+
 #ifdef CONFIG_EFI
 # ifdef CONFIG_X86
-   extern int efi_enabled;
+extern int efi_enabled(int facility);
 # else
-#  define efi_enabled 1
+static inline int efi_enabled(int facility)
+{
+       return 1;
+}
 # endif
 #else
-# define efi_enabled 0
+static inline int efi_enabled(int facility)
+{
+       return 0;
+}
 #endif
 
 /*
index cb08fea2aa0b754832cda6f309bb8611996fc254..5d0eb1d2af8f529d3abeefbfac85094eddaf7ec2 100644 (file)
@@ -606,7 +606,7 @@ asmlinkage void __init start_kernel(void)
        pidmap_init();
        anon_vma_init();
 #ifdef CONFIG_X86
-       if (efi_enabled)
+       if (efi_enabled(EFI_RUNTIME_SERVICES))
                efi_enter_virtual_mode();
 #endif
        thread_info_cache_init();