Merge branch 'x86-efi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 5 Aug 2014 00:13:50 +0000 (17:13 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 5 Aug 2014 00:13:50 +0000 (17:13 -0700)
Pull EFI changes from Ingo Molnar:
 "Main changes in this cycle are:

   - arm64 efi stub fixes, preservation of FP/SIMD registers across
     firmware calls, and conversion of the EFI stub code into a static
     library - Ard Biesheuvel

   - Xen EFI support - Daniel Kiper

   - Support for autoloading the efivars driver - Lee, Chun-Yi

   - Use the PE/COFF headers in the x86 EFI boot stub to request that
     the stub be loaded with CONFIG_PHYSICAL_ALIGN alignment - Michael
     Brown

   - Consolidate all the x86 EFI quirks into one file - Saurabh Tangri

   - Additional error logging in x86 EFI boot stub - Ulf Winkelvos

   - Support loading initrd above 4G in EFI boot stub - Yinghai Lu

   - EFI reboot patches for ACPI hardware reduced platforms"

* 'x86-efi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (31 commits)
  efi/arm64: Handle missing virtual mapping for UEFI System Table
  arch/x86/xen: Silence compiler warnings
  xen: Silence compiler warnings
  x86/efi: Request desired alignment via the PE/COFF headers
  x86/efi: Add better error logging to EFI boot stub
  efi: Autoload efivars
  efi: Update stale locking comment for struct efivars
  arch/x86: Remove efi_set_rtc_mmss()
  arch/x86: Replace plain strings with constants
  xen: Put EFI machinery in place
  xen: Define EFI related stuff
  arch/x86: Remove redundant set_bit(EFI_MEMMAP) call
  arch/x86: Remove redundant set_bit(EFI_SYSTEM_TABLES) call
  efi: Introduce EFI_PARAVIRT flag
  arch/x86: Do not access EFI memory map if it is not available
  efi: Use early_mem*() instead of early_io*()
  arch/ia64: Define early_memunmap()
  x86/reboot: Add EFI reboot quirk for ACPI Hardware Reduced flag
  efi/reboot: Allow powering off machines using EFI
  efi/reboot: Add generic wrapper around EfiResetSystem()
  ...

40 files changed:
arch/arm64/Kconfig
arch/arm64/Makefile
arch/arm64/include/asm/efi.h
arch/arm64/kernel/Makefile
arch/arm64/kernel/efi-stub.c
arch/arm64/kernel/efi.c
arch/ia64/include/asm/io.h
arch/ia64/kernel/process.c
arch/x86/Kconfig
arch/x86/boot/compressed/Makefile
arch/x86/boot/compressed/eboot.c
arch/x86/boot/compressed/eboot.h
arch/x86/boot/header.S
arch/x86/include/asm/efi.h
arch/x86/kernel/reboot.c
arch/x86/kernel/setup.c
arch/x86/platform/efi/Makefile
arch/x86/platform/efi/efi.c
arch/x86/platform/efi/quirks.c [new file with mode: 0644]
arch/x86/xen/Makefile
arch/x86/xen/efi.c [new file with mode: 0644]
arch/x86/xen/enlighten.c
arch/x86/xen/xen-ops.h
drivers/firmware/efi/Kconfig
drivers/firmware/efi/Makefile
drivers/firmware/efi/efi.c
drivers/firmware/efi/efivars.c
drivers/firmware/efi/libstub/Makefile [new file with mode: 0644]
drivers/firmware/efi/libstub/arm-stub.c [moved from drivers/firmware/efi/arm-stub.c with 93% similarity]
drivers/firmware/efi/libstub/efi-stub-helper.c [moved from drivers/firmware/efi/efi-stub-helper.c with 88% similarity]
drivers/firmware/efi/libstub/efistub.h [new file with mode: 0644]
drivers/firmware/efi/libstub/fdt.c [moved from drivers/firmware/efi/fdt.c with 94% similarity]
drivers/firmware/efi/reboot.c [new file with mode: 0644]
drivers/firmware/efi/runtime-wrappers.c [new file with mode: 0644]
drivers/xen/Kconfig
drivers/xen/Makefile
drivers/xen/efi.c [new file with mode: 0644]
include/linux/efi.h
include/xen/interface/platform.h
include/xen/xen-ops.h

index f3b584b..b0f9c9d 100644 (file)
@@ -347,12 +347,18 @@ config CMDLINE_FORCE
          This is useful if you cannot or don't want to change the
          command-line options your boot loader passes to the kernel.
 
+config EFI_STUB
+       bool
+
 config EFI
        bool "UEFI runtime support"
        depends on OF && !CPU_BIG_ENDIAN
        select LIBFDT
        select UCS2_STRING
        select EFI_PARAMS_FROM_FDT
+       select EFI_RUNTIME_WRAPPERS
+       select EFI_STUB
+       select EFI_ARMSTUB
        default y
        help
          This option provides support for runtime services provided
index e8d025c..5783354 100644 (file)
@@ -52,6 +52,7 @@ core-$(CONFIG_XEN) += arch/arm64/xen/
 core-$(CONFIG_CRYPTO) += arch/arm64/crypto/
 libs-y         := arch/arm64/lib/ $(libs-y)
 libs-y         += $(LIBGCC)
+libs-$(CONFIG_EFI_STUB) += drivers/firmware/efi/libstub/
 
 # Default target when executing plain make
 KBUILD_IMAGE   := Image.gz
index 5a46c4e..a34fd3b 100644 (file)
@@ -2,6 +2,7 @@
 #define _ASM_EFI_H
 
 #include <asm/io.h>
+#include <asm/neon.h>
 
 #ifdef CONFIG_EFI
 extern void efi_init(void);
@@ -11,4 +12,36 @@ extern void efi_idmap_init(void);
 #define efi_idmap_init()
 #endif
 
+#define efi_call_virt(f, ...)                                          \
+({                                                                     \
+       efi_##f##_t *__f = efi.systab->runtime->f;                      \
+       efi_status_t __s;                                               \
+                                                                       \
+       kernel_neon_begin();                                            \
+       __s = __f(__VA_ARGS__);                                         \
+       kernel_neon_end();                                              \
+       __s;                                                            \
+})
+
+#define __efi_call_virt(f, ...)                                                \
+({                                                                     \
+       efi_##f##_t *__f = efi.systab->runtime->f;                      \
+                                                                       \
+       kernel_neon_begin();                                            \
+       __f(__VA_ARGS__);                                               \
+       kernel_neon_end();                                              \
+})
+
+/* arch specific definitions used by the stub code */
+
+/*
+ * AArch64 requires the DTB to be 8-byte aligned in the first 512MiB from
+ * start of kernel and may not cross a 2MiB boundary. We set alignment to
+ * 2MiB so we know it won't cross a 2MiB boundary.
+ */
+#define EFI_FDT_ALIGN  SZ_2M   /* used by allocate_new_fdt_and_exit_boot() */
+#define MAX_FDT_OFFSET SZ_512M
+
+#define efi_call_early(f, ...) sys_table_arg->boottime->f(__VA_ARGS__)
+
 #endif /* _ASM_EFI_H */
index 27c72ef..df7ef87 100644 (file)
@@ -4,8 +4,7 @@
 
 CPPFLAGS_vmlinux.lds   := -DTEXT_OFFSET=$(TEXT_OFFSET)
 AFLAGS_head.o          := -DTEXT_OFFSET=$(TEXT_OFFSET)
-CFLAGS_efi-stub.o      := -DTEXT_OFFSET=$(TEXT_OFFSET) \
-                          -I$(src)/../../../scripts/dtc/libfdt
+CFLAGS_efi-stub.o      := -DTEXT_OFFSET=$(TEXT_OFFSET)
 
 CFLAGS_REMOVE_ftrace.o = -pg
 CFLAGS_REMOVE_insn.o = -pg
index e786e6c..1317fef 100644 (file)
  *
  */
 #include <linux/efi.h>
-#include <linux/libfdt.h>
+#include <asm/efi.h>
 #include <asm/sections.h>
 
-/*
- * AArch64 requires the DTB to be 8-byte aligned in the first 512MiB from
- * start of kernel and may not cross a 2MiB boundary. We set alignment to
- * 2MiB so we know it won't cross a 2MiB boundary.
- */
-#define EFI_FDT_ALIGN  SZ_2M   /* used by allocate_new_fdt_and_exit_boot() */
-#define MAX_FDT_OFFSET SZ_512M
-
-#define efi_call_early(f, ...) sys_table_arg->boottime->f(__VA_ARGS__)
-
-static void efi_char16_printk(efi_system_table_t *sys_table_arg,
-                             efi_char16_t *str);
-
-static efi_status_t efi_open_volume(efi_system_table_t *sys_table,
-                                   void *__image, void **__fh);
-static efi_status_t efi_file_close(void *handle);
-
-static efi_status_t
-efi_file_read(void *handle, unsigned long *size, void *addr);
-
-static efi_status_t
-efi_file_size(efi_system_table_t *sys_table, void *__fh,
-             efi_char16_t *filename_16, void **handle, u64 *file_sz);
-
-/* Include shared EFI stub code */
-#include "../../../drivers/firmware/efi/efi-stub-helper.c"
-#include "../../../drivers/firmware/efi/fdt.c"
-#include "../../../drivers/firmware/efi/arm-stub.c"
-
-
-static efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
-                                       unsigned long *image_addr,
-                                       unsigned long *image_size,
-                                       unsigned long *reserve_addr,
-                                       unsigned long *reserve_size,
-                                       unsigned long dram_base,
-                                       efi_loaded_image_t *image)
+efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
+                                unsigned long *image_addr,
+                                unsigned long *image_size,
+                                unsigned long *reserve_addr,
+                                unsigned long *reserve_size,
+                                unsigned long dram_base,
+                                efi_loaded_image_t *image)
 {
        efi_status_t status;
        unsigned long kernel_size, kernel_memsize = 0;
@@ -69,7 +39,7 @@ static efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
                if (*image_addr != (dram_base + TEXT_OFFSET)) {
                        pr_efi_err(sys_table, "Failed to alloc kernel memory\n");
                        efi_free(sys_table, kernel_memsize, *image_addr);
-                       return EFI_ERROR;
+                       return EFI_LOAD_ERROR;
                }
                *image_size = kernel_memsize;
        }
index 14db1f6..e72f310 100644 (file)
@@ -414,13 +414,24 @@ static int __init arm64_enter_virtual_mode(void)
        for_each_efi_memory_desc(&memmap, md) {
                if (!(md->attribute & EFI_MEMORY_RUNTIME))
                        continue;
-               if (remap_region(md, &virt_md))
-                       ++count;
+               if (!remap_region(md, &virt_md))
+                       goto err_unmap;
+               ++count;
        }
 
        efi.systab = (__force void *)efi_lookup_mapped_addr(efi_system_table);
-       if (efi.systab)
-               set_bit(EFI_SYSTEM_TABLES, &efi.flags);
+       if (!efi.systab) {
+               /*
+                * If we have no virtual mapping for the System Table at this
+                * point, the memory map doesn't cover the physical offset where
+                * it resides. This means the System Table will be inaccessible
+                * to Runtime Services themselves once the virtual mapping is
+                * installed.
+                */
+               pr_err("Failed to remap EFI System Table -- buggy firmware?\n");
+               goto err_unmap;
+       }
+       set_bit(EFI_SYSTEM_TABLES, &efi.flags);
 
        local_irq_save(flags);
        cpu_switch_mm(idmap_pg_dir, &init_mm);
@@ -449,21 +460,18 @@ static int __init arm64_enter_virtual_mode(void)
 
        /* Set up runtime services function pointers */
        runtime = efi.systab->runtime;
-       efi.get_time = runtime->get_time;
-       efi.set_time = runtime->set_time;
-       efi.get_wakeup_time = runtime->get_wakeup_time;
-       efi.set_wakeup_time = runtime->set_wakeup_time;
-       efi.get_variable = runtime->get_variable;
-       efi.get_next_variable = runtime->get_next_variable;
-       efi.set_variable = runtime->set_variable;
-       efi.query_variable_info = runtime->query_variable_info;
-       efi.update_capsule = runtime->update_capsule;
-       efi.query_capsule_caps = runtime->query_capsule_caps;
-       efi.get_next_high_mono_count = runtime->get_next_high_mono_count;
-       efi.reset_system = runtime->reset_system;
-
+       efi_native_runtime_setup();
        set_bit(EFI_RUNTIME_SERVICES, &efi.flags);
 
        return 0;
+
+err_unmap:
+       /* unmap all mappings that succeeded: there are 'count' of those */
+       for (virt_md = virtmap; count--; virt_md += memmap.desc_size) {
+               md = virt_md;
+               iounmap((__force void __iomem *)md->virt_addr);
+       }
+       kfree(virtmap);
+       return -1;
 }
 early_initcall(arm64_enter_virtual_mode);
index 0d2bcb3..bee0acd 100644 (file)
@@ -426,6 +426,7 @@ extern void iounmap (volatile void __iomem *addr);
 extern void __iomem * early_ioremap (unsigned long phys_addr, unsigned long size);
 #define early_memremap(phys_addr, size)        early_ioremap(phys_addr, size)
 extern void early_iounmap (volatile void __iomem *addr, unsigned long size);
+#define early_memunmap(addr, size)             early_iounmap(addr, size)
 static inline void __iomem * ioremap_cache (unsigned long phys_addr, unsigned long size)
 {
        return ioremap(phys_addr, size);
index 55d4ba4..deed6fa 100644 (file)
@@ -662,7 +662,7 @@ void
 machine_restart (char *restart_cmd)
 {
        (void) notify_die(DIE_MACHINE_RESTART, restart_cmd, NULL, 0, 0, 0);
-       (*efi.reset_system)(EFI_RESET_WARM, 0, 0, NULL);
+       efi_reboot(REBOOT_WARM, NULL);
 }
 
 void
index 2840c27..3fc7d72 100644 (file)
@@ -1522,6 +1522,7 @@ config EFI
        bool "EFI runtime service support"
        depends on ACPI
        select UCS2_STRING
+       select EFI_RUNTIME_WRAPPERS
        ---help---
          This enables the kernel to use EFI runtime services that are
          available (such as the EFI variable services).
index 0fcd913..7a801a3 100644 (file)
@@ -33,7 +33,8 @@ VMLINUX_OBJS = $(obj)/vmlinux.lds $(obj)/head_$(BITS).o $(obj)/misc.o \
 $(obj)/eboot.o: KBUILD_CFLAGS += -fshort-wchar -mno-red-zone
 
 ifeq ($(CONFIG_EFI_STUB), y)
-       VMLINUX_OBJS += $(obj)/eboot.o $(obj)/efi_stub_$(BITS).o
+       VMLINUX_OBJS += $(obj)/eboot.o $(obj)/efi_stub_$(BITS).o \
+                               $(objtree)/drivers/firmware/efi/libstub/lib.a
 endif
 
 $(obj)/vmlinux: $(VMLINUX_OBJS) FORCE
index 0331d76..f277184 100644 (file)
 
 static efi_system_table_t *sys_table;
 
-static struct efi_config *efi_early;
-
-#define efi_call_early(f, ...)                                         \
-       efi_early->call(efi_early->f, __VA_ARGS__);
+struct efi_config *efi_early;
 
 #define BOOT_SERVICES(bits)                                            \
 static void setup_boot_services##bits(struct efi_config *c)            \
@@ -48,8 +45,7 @@ static void setup_boot_services##bits(struct efi_config *c)           \
 BOOT_SERVICES(32);
 BOOT_SERVICES(64);
 
-static void efi_printk(efi_system_table_t *, char *);
-static void efi_char16_printk(efi_system_table_t *, efi_char16_t *);
+void efi_char16_printk(efi_system_table_t *, efi_char16_t *);
 
 static efi_status_t
 __file_size32(void *__fh, efi_char16_t *filename_16,
@@ -156,7 +152,7 @@ grow:
 
        return status;
 }
-static efi_status_t
+efi_status_t
 efi_file_size(efi_system_table_t *sys_table, void *__fh,
              efi_char16_t *filename_16, void **handle, u64 *file_sz)
 {
@@ -166,7 +162,7 @@ efi_file_size(efi_system_table_t *sys_table, void *__fh,
        return __file_size32(__fh, filename_16, handle, file_sz);
 }
 
-static inline efi_status_t
+efi_status_t
 efi_file_read(void *handle, unsigned long *size, void *addr)
 {
        unsigned long func;
@@ -184,7 +180,7 @@ efi_file_read(void *handle, unsigned long *size, void *addr)
        }
 }
 
-static inline efi_status_t efi_file_close(void *handle)
+efi_status_t efi_file_close(void *handle)
 {
        if (efi_early->is64) {
                efi_file_handle_64_t *fh = handle;
@@ -249,7 +245,7 @@ static inline efi_status_t __open_volume64(void *__image, void **__fh)
        return status;
 }
 
-static inline efi_status_t
+efi_status_t
 efi_open_volume(efi_system_table_t *sys_table, void *__image, void **__fh)
 {
        if (efi_early->is64)
@@ -258,7 +254,7 @@ efi_open_volume(efi_system_table_t *sys_table, void *__image, void **__fh)
        return __open_volume32(__image, __fh);
 }
 
-static void efi_char16_printk(efi_system_table_t *table, efi_char16_t *str)
+void efi_char16_printk(efi_system_table_t *table, efi_char16_t *str)
 {
        unsigned long output_string;
        size_t offset;
@@ -284,8 +280,6 @@ static void efi_char16_printk(efi_system_table_t *table, efi_char16_t *str)
        }
 }
 
-#include "../../../../drivers/firmware/efi/efi-stub-helper.c"
-
 static void find_bits(unsigned long mask, u8 *pos, u8 *size)
 {
        u8 first, len;
@@ -1038,6 +1032,7 @@ struct boot_params *make_boot_params(struct efi_config *c)
        int i;
        unsigned long ramdisk_addr;
        unsigned long ramdisk_size;
+       unsigned long initrd_addr_max;
 
        efi_early = c;
        sys_table = (efi_system_table_t *)(unsigned long)efi_early->table;
@@ -1100,14 +1095,21 @@ struct boot_params *make_boot_params(struct efi_config *c)
 
        memset(sdt, 0, sizeof(*sdt));
 
+       if (hdr->xloadflags & XLF_CAN_BE_LOADED_ABOVE_4G)
+               initrd_addr_max = -1UL;
+       else
+               initrd_addr_max = hdr->initrd_addr_max;
+
        status = handle_cmdline_files(sys_table, image,
                                      (char *)(unsigned long)hdr->cmd_line_ptr,
-                                     "initrd=", hdr->initrd_addr_max,
+                                     "initrd=", initrd_addr_max,
                                      &ramdisk_addr, &ramdisk_size);
        if (status != EFI_SUCCESS)
                goto fail2;
-       hdr->ramdisk_image = ramdisk_addr;
-       hdr->ramdisk_size = ramdisk_size;
+       hdr->ramdisk_image = ramdisk_addr & 0xffffffff;
+       hdr->ramdisk_size  = ramdisk_size & 0xffffffff;
+       boot_params->ext_ramdisk_image = (u64)ramdisk_addr >> 32;
+       boot_params->ext_ramdisk_size  = (u64)ramdisk_size >> 32;
 
        return boot_params;
 fail2:
@@ -1374,7 +1376,10 @@ struct boot_params *efi_main(struct efi_config *c,
 
        setup_graphics(boot_params);
 
-       setup_efi_pci(boot_params);
+       status = setup_efi_pci(boot_params);
+       if (status != EFI_SUCCESS) {
+               efi_printk(sys_table, "setup_efi_pci() failed!\n");
+       }
 
        status = efi_call_early(allocate_pool, EFI_LOADER_DATA,
                                sizeof(*gdt), (void **)&gdt);
@@ -1401,16 +1406,20 @@ struct boot_params *efi_main(struct efi_config *c,
                                             hdr->init_size, hdr->init_size,
                                             hdr->pref_address,
                                             hdr->kernel_alignment);
-               if (status != EFI_SUCCESS)
+               if (status != EFI_SUCCESS) {
+                       efi_printk(sys_table, "efi_relocate_kernel() failed!\n");
                        goto fail;
+               }
 
                hdr->pref_address = hdr->code32_start;
                hdr->code32_start = bzimage_addr;
        }
 
        status = exit_boot(boot_params, handle, is64);
-       if (status != EFI_SUCCESS)
+       if (status != EFI_SUCCESS) {
+               efi_printk(sys_table, "exit_boot() failed!\n");
                goto fail;
+       }
 
        memset((char *)gdt->address, 0x0, gdt->size);
        desc = (struct desc_struct *)gdt->address;
@@ -1470,5 +1479,6 @@ struct boot_params *efi_main(struct efi_config *c,
 
        return boot_params;
 fail:
+       efi_printk(sys_table, "efi_main() failed!\n");
        return NULL;
 }
index c88c31e..d487e72 100644 (file)
@@ -103,20 +103,4 @@ struct efi_uga_draw_protocol {
        void *blt;
 };
 
-struct efi_config {
-       u64 image_handle;
-       u64 table;
-       u64 allocate_pool;
-       u64 allocate_pages;
-       u64 get_memory_map;
-       u64 free_pool;
-       u64 free_pages;
-       u64 locate_handle;
-       u64 handle_protocol;
-       u64 exit_boot_services;
-       u64 text_output;
-       efi_status_t (*call)(unsigned long, ...);
-       bool is64;
-} __packed;
-
 #endif /* BOOT_COMPRESSED_EBOOT_H */
index 7a6d43a..16ef025 100644 (file)
@@ -154,7 +154,7 @@ extra_header_fields:
 #else
        .quad   0                               # ImageBase
 #endif
-       .long   0x20                            # SectionAlignment
+       .long   CONFIG_PHYSICAL_ALIGN           # SectionAlignment
        .long   0x20                            # FileAlignment
        .word   0                               # MajorOperatingSystemVersion
        .word   0                               # MinorOperatingSystemVersion
index 1eb5f64..044a2fd 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;
@@ -156,6 +158,33 @@ static inline efi_status_t efi_thunk_set_virtual_address_map(
        return EFI_SUCCESS;
 }
 #endif /* CONFIG_EFI_MIXED */
+
+
+/* arch specific definitions used by the stub code */
+
+struct efi_config {
+       u64 image_handle;
+       u64 table;
+       u64 allocate_pool;
+       u64 allocate_pages;
+       u64 get_memory_map;
+       u64 free_pool;
+       u64 free_pages;
+       u64 locate_handle;
+       u64 handle_protocol;
+       u64 exit_boot_services;
+       u64 text_output;
+       efi_status_t (*call)(unsigned long, ...);
+       bool is64;
+} __packed;
+
+extern struct efi_config *efi_early;
+
+#define efi_call_early(f, ...)                                         \
+       efi_early->call(efi_early->f, __VA_ARGS__);
+
+extern bool efi_reboot_required(void);
+
 #else
 /*
  * IF EFI is not configured, have the EFI calls return -ENOSYS.
@@ -168,6 +197,10 @@ static inline efi_status_t efi_thunk_set_virtual_address_map(
 #define efi_call5(_f, _a1, _a2, _a3, _a4, _a5)         (-ENOSYS)
 #define efi_call6(_f, _a1, _a2, _a3, _a4, _a5, _a6)    (-ENOSYS)
 static inline void parse_efi_setup(u64 phys_addr, u32 data_len) {}
+static inline bool efi_reboot_required(void)
+{
+       return false;
+}
 #endif /* CONFIG_EFI */
 
 #endif /* _ASM_X86_EFI_H */
index 52b1157..17962e6 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/mc146818rtc.h>
 #include <asm/realmode.h>
 #include <asm/x86_init.h>
+#include <asm/efi.h>
 
 /*
  * Power off function, if any
@@ -401,12 +402,25 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = {
 
 static int __init reboot_init(void)
 {
+       int rv;
+
        /*
         * Only do the DMI check if reboot_type hasn't been overridden
         * on the command line
         */
-       if (reboot_default)
-               dmi_check_system(reboot_dmi_table);
+       if (!reboot_default)
+               return 0;
+
+       /*
+        * The DMI quirks table takes precedence. If no quirks entry
+        * matches and the ACPI Hardware Reduced bit is set, force EFI
+        * reboot.
+        */
+       rv = dmi_check_system(reboot_dmi_table);
+
+       if (!rv && efi_reboot_required())
+               reboot_type = BOOT_EFI;
+
        return 0;
 }
 core_initcall(reboot_init);
@@ -528,11 +542,7 @@ static void native_machine_emergency_restart(void)
                        break;
 
                case BOOT_EFI:
-                       if (efi_enabled(EFI_RUNTIME_SERVICES))
-                               efi.reset_system(reboot_mode == REBOOT_WARM ?
-                                                EFI_RESET_WARM :
-                                                EFI_RESET_COLD,
-                                                EFI_SUCCESS, 0, NULL);
+                       efi_reboot(reboot_mode, NULL);
                        reboot_type = BOOT_BIOS;
                        break;
 
index 78a0e62..41ead8d 100644 (file)
@@ -924,10 +924,10 @@ void __init setup_arch(char **cmdline_p)
 #endif
 #ifdef CONFIG_EFI
        if (!strncmp((char *)&boot_params.efi_info.efi_loader_signature,
-                    "EL32", 4)) {
+                    EFI32_LOADER_SIGNATURE, 4)) {
                set_bit(EFI_BOOT, &efi.flags);
        } else if (!strncmp((char *)&boot_params.efi_info.efi_loader_signature,
-                    "EL64", 4)) {
+                    EFI64_LOADER_SIGNATURE, 4)) {
                set_bit(EFI_BOOT, &efi.flags);
                set_bit(EFI_64BIT, &efi.flags);
        }
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..850da94 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,139 +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;
-       efi_status_t status;
-
-       spin_lock_irqsave(&rtc_lock, flags);
-       status = efi_call_virt(get_time, tm, tc);
-       spin_unlock_irqrestore(&rtc_lock, flags);
-       return status;
-}
-
-static efi_status_t virt_efi_set_time(efi_time_t *tm)
-{
-       unsigned long flags;
-       efi_status_t status;
-
-       spin_lock_irqsave(&rtc_lock, flags);
-       status = efi_call_virt(set_time, tm);
-       spin_unlock_irqrestore(&rtc_lock, flags);
-       return status;
-}
-
-static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled,
-                                            efi_bool_t *pending,
-                                            efi_time_t *tm)
-{
-       unsigned long flags;
-       efi_status_t status;
-
-       spin_lock_irqsave(&rtc_lock, flags);
-       status = efi_call_virt(get_wakeup_time, enabled, pending, tm);
-       spin_unlock_irqrestore(&rtc_lock, flags);
-       return status;
-}
-
-static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm)
-{
-       unsigned long flags;
-       efi_status_t status;
-
-       spin_lock_irqsave(&rtc_lock, flags);
-       status = efi_call_virt(set_wakeup_time, enabled, tm);
-       spin_unlock_irqrestore(&rtc_lock, flags);
-       return status;
-}
-
-static efi_status_t virt_efi_get_variable(efi_char16_t *name,
-                                         efi_guid_t *vendor,
-                                         u32 *attr,
-                                         unsigned long *data_size,
-                                         void *data)
-{
-       return efi_call_virt(get_variable,
-                            name, vendor, attr,
-                            data_size, data);
-}
-
-static efi_status_t virt_efi_get_next_variable(unsigned long *name_size,
-                                              efi_char16_t *name,
-                                              efi_guid_t *vendor)
-{
-       return efi_call_virt(get_next_variable,
-                            name_size, name, vendor);
-}
-
-static efi_status_t virt_efi_set_variable(efi_char16_t *name,
-                                         efi_guid_t *vendor,
-                                         u32 attr,
-                                         unsigned long data_size,
-                                         void *data)
-{
-       return efi_call_virt(set_variable,
-                            name, vendor, attr,
-                            data_size, data);
-}
-
-static efi_status_t virt_efi_query_variable_info(u32 attr,
-                                                u64 *storage_space,
-                                                u64 *remaining_space,
-                                                u64 *max_variable_size)
-{
-       if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
-               return EFI_UNSUPPORTED;
-
-       return efi_call_virt(query_variable_info, attr, storage_space,
-                            remaining_space, max_variable_size);
-}
-
-static efi_status_t virt_efi_get_next_high_mono_count(u32 *count)
-{
-       return efi_call_virt(get_next_high_mono_count, count);
-}
-
-static void virt_efi_reset_system(int reset_type,
-                                 efi_status_t status,
-                                 unsigned long data_size,
-                                 efi_char16_t *data)
-{
-       __efi_call_virt(reset_system, reset_type, status,
-                       data_size, data);
-}
-
-static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules,
-                                           unsigned long count,
-                                           unsigned long sg_list)
-{
-       if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
-               return EFI_UNSUPPORTED;
-
-       return efi_call_virt(update_capsule, capsules, count, sg_list);
-}
-
-static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules,
-                                               unsigned long count,
-                                               u64 *max_size,
-                                               int *reset_type)
-{
-       if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
-               return EFI_UNSUPPORTED;
-
-       return efi_call_virt(query_capsule_caps, capsules, count, max_size,
-                            reset_type);
-}
-
 static efi_status_t __init phys_efi_set_virtual_address_map(
        unsigned long memory_map_size,
        unsigned long descriptor_size,
@@ -244,42 +104,6 @@ static efi_status_t __init phys_efi_set_virtual_address_map(
        return status;
 }
 
-int efi_set_rtc_mmss(const struct timespec *now)
-{
-       unsigned long nowtime = now->tv_sec;
-       efi_status_t    status;
-       efi_time_t      eft;
-       efi_time_cap_t  cap;
-       struct rtc_time tm;
-
-       status = efi.get_time(&eft, &cap);
-       if (status != EFI_SUCCESS) {
-               pr_err("Oops: efitime: can't read time!\n");
-               return -1;
-       }
-
-       rtc_time_to_tm(nowtime, &tm);
-       if (!rtc_valid_tm(&tm)) {
-               eft.year = tm.tm_year + 1900;
-               eft.month = tm.tm_mon + 1;
-               eft.day = tm.tm_mday;
-               eft.minute = tm.tm_min;
-               eft.second = tm.tm_sec;
-               eft.nanosecond = 0;
-       } else {
-               pr_err("%s: Invalid EFI RTC value: write of %lx to EFI RTC failed\n",
-                      __func__, nowtime);
-               return -1;
-       }
-
-       status = efi.set_time(&eft);
-       if (status != EFI_SUCCESS) {
-               pr_err("Oops: efitime: can't write time!\n");
-               return -1;
-       }
-       return 0;
-}
-
 void efi_get_time(struct timespec *now)
 {
        efi_status_t status;
@@ -350,6 +174,9 @@ int __init efi_memblock_x86_reserve_range(void)
        struct efi_info *e = &boot_params.efi_info;
        unsigned long pmap;
 
+       if (efi_enabled(EFI_PARAVIRT))
+               return 0;
+
 #ifdef CONFIG_X86_32
        /* Can't handle data above 4GB at this time */
        if (e->efi_memmap_hi) {
@@ -392,69 +219,15 @@ 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);
        if (memmap.map) {
-               early_iounmap(memmap.map, memmap.nr_map * memmap.desc_size);
+               early_memunmap(memmap.map, memmap.nr_map * memmap.desc_size);
                memmap.map = NULL;
        }
 }
 
-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)) {
@@ -467,12 +240,12 @@ static int __init efi_systab_init(void *phys)
                        if (!data)
                                return -ENOMEM;
                }
-               systab64 = early_ioremap((unsigned long)phys,
+               systab64 = early_memremap((unsigned long)phys,
                                         sizeof(*systab64));
                if (systab64 == NULL) {
                        pr_err("Couldn't map the system table!\n");
                        if (data)
-                               early_iounmap(data, sizeof(*data));
+                               early_memunmap(data, sizeof(*data));
                        return -ENOMEM;
                }
 
@@ -504,9 +277,9 @@ static int __init efi_systab_init(void *phys)
                                           systab64->tables;
                tmp |= data ? data->tables : systab64->tables;
 
-               early_iounmap(systab64, sizeof(*systab64));
+               early_memunmap(systab64, sizeof(*systab64));
                if (data)
-                       early_iounmap(data, sizeof(*data));
+                       early_memunmap(data, sizeof(*data));
 #ifdef CONFIG_X86_32
                if (tmp >> 32) {
                        pr_err("EFI data located above 4GB, disabling EFI.\n");
@@ -516,7 +289,7 @@ static int __init efi_systab_init(void *phys)
        } else {
                efi_system_table_32_t *systab32;
 
-               systab32 = early_ioremap((unsigned long)phys,
+               systab32 = early_memremap((unsigned long)phys,
                                         sizeof(*systab32));
                if (systab32 == NULL) {
                        pr_err("Couldn't map the system table!\n");
@@ -537,7 +310,7 @@ static int __init efi_systab_init(void *phys)
                efi_systab.nr_tables = systab32->nr_tables;
                efi_systab.tables = systab32->tables;
 
-               early_iounmap(systab32, sizeof(*systab32));
+               early_memunmap(systab32, sizeof(*systab32));
        }
 
        efi.systab = &efi_systab;
@@ -563,7 +336,7 @@ static int __init efi_runtime_init32(void)
 {
        efi_runtime_services_32_t *runtime;
 
-       runtime = early_ioremap((unsigned long)efi.systab->runtime,
+       runtime = early_memremap((unsigned long)efi.systab->runtime,
                        sizeof(efi_runtime_services_32_t));
        if (!runtime) {
                pr_err("Could not map the runtime service table!\n");
@@ -578,7 +351,7 @@ static int __init efi_runtime_init32(void)
        efi_phys.set_virtual_address_map =
                        (efi_set_virtual_address_map_t *)
                        (unsigned long)runtime->set_virtual_address_map;
-       early_iounmap(runtime, sizeof(efi_runtime_services_32_t));
+       early_memunmap(runtime, sizeof(efi_runtime_services_32_t));
 
        return 0;
 }
@@ -587,7 +360,7 @@ static int __init efi_runtime_init64(void)
 {
        efi_runtime_services_64_t *runtime;
 
-       runtime = early_ioremap((unsigned long)efi.systab->runtime,
+       runtime = early_memremap((unsigned long)efi.systab->runtime,
                        sizeof(efi_runtime_services_64_t));
        if (!runtime) {
                pr_err("Could not map the runtime service table!\n");
@@ -602,7 +375,7 @@ static int __init efi_runtime_init64(void)
        efi_phys.set_virtual_address_map =
                        (efi_set_virtual_address_map_t *)
                        (unsigned long)runtime->set_virtual_address_map;
-       early_iounmap(runtime, sizeof(efi_runtime_services_64_t));
+       early_memunmap(runtime, sizeof(efi_runtime_services_64_t));
 
        return 0;
 }
@@ -616,14 +389,24 @@ static int __init efi_runtime_init(void)
         * 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.
+        *
+        * When EFI_PARAVIRT is in force then we could not map runtime
+        * service memory region because we do not have direct access to it.
+        * However, runtime services are available through proxy functions
+        * (e.g. in case of Xen dom0 EFI implementation they call special
+        * hypercall which executes relevant EFI functions) and that is why
+        * they are always enabled.
         */
-       if (efi_enabled(EFI_64BIT))
-               rv = efi_runtime_init64();
-       else
-               rv = efi_runtime_init32();
 
-       if (rv)
-               return rv;
+       if (!efi_enabled(EFI_PARAVIRT)) {
+               if (efi_enabled(EFI_64BIT))
+                       rv = efi_runtime_init64();
+               else
+                       rv = efi_runtime_init32();
+
+               if (rv)
+                       return rv;
+       }
 
        set_bit(EFI_RUNTIME_SERVICES, &efi.flags);
 
@@ -632,8 +415,11 @@ static int __init efi_runtime_init(void)
 
 static int __init efi_memmap_init(void)
 {
+       if (efi_enabled(EFI_PARAVIRT))
+               return 0;
+
        /* Map the EFI memory map */
-       memmap.map = early_ioremap((unsigned long)memmap.phys_map,
+       memmap.map = early_memremap((unsigned long)memmap.phys_map,
                                   memmap.nr_map * memmap.desc_size);
        if (memmap.map == NULL) {
                pr_err("Could not map the memory map!\n");
@@ -649,62 +435,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;
@@ -728,8 +458,6 @@ void __init efi_init(void)
        if (efi_systab_init(efi_phys.systab))
                return;
 
-       set_bit(EFI_SYSTEM_TABLES, &efi.flags);
-
        efi.config_table = (unsigned long)efi.systab->tables;
        efi.fw_vendor    = (unsigned long)efi.systab->fw_vendor;
        efi.runtime      = (unsigned long)efi.systab->runtime;
@@ -737,14 +465,14 @@ void __init efi_init(void)
        /*
         * Show what we know for posterity
         */
-       c16 = tmp = early_ioremap(efi.systab->fw_vendor, 2);
+       c16 = tmp = early_memremap(efi.systab->fw_vendor, 2);
        if (c16) {
                for (i = 0; i < sizeof(vendor) - 1 && *c16; ++i)
                        vendor[i] = *c16++;
                vendor[i] = '\0';
        } else
                pr_err("Could not map the firmware vendor!\n");
-       early_iounmap(tmp, 2);
+       early_memunmap(tmp, 2);
 
        pr_info("EFI v%u.%.02u by %s\n",
                efi.systab->hdr.revision >> 16,
@@ -770,8 +498,6 @@ void __init efi_init(void)
        if (efi_memmap_init())
                return;
 
-       set_bit(EFI_MEMMAP, &efi.flags);
-
        print_efi_memmap();
 }
 
@@ -847,22 +573,6 @@ void __init old_map_region(efi_memory_desc_t *md)
                       (unsigned long long)md->phys_addr);
 }
 
-static void native_runtime_setup(void)
-{
-       efi.get_time = virt_efi_get_time;
-       efi.set_time = virt_efi_set_time;
-       efi.get_wakeup_time = virt_efi_get_wakeup_time;
-       efi.set_wakeup_time = virt_efi_set_wakeup_time;
-       efi.get_variable = virt_efi_get_variable;
-       efi.get_next_variable = virt_efi_get_next_variable;
-       efi.set_variable = virt_efi_set_variable;
-       efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count;
-       efi.reset_system = virt_efi_reset_system;
-       efi.query_variable_info = virt_efi_query_variable_info;
-       efi.update_capsule = virt_efi_update_capsule;
-       efi.query_capsule_caps = virt_efi_query_capsule_caps;
-}
-
 /* Merge contiguous regions of the same type and attribute */
 static void __init efi_merge_regions(void)
 {
@@ -1049,7 +759,7 @@ static void __init kexec_enter_virtual_mode(void)
         */
        efi.runtime_version = efi_systab.hdr.revision;
 
-       native_runtime_setup();
+       efi_native_runtime_setup();
 
        efi.set_virtual_address_map = NULL;
 
@@ -1057,11 +767,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
 }
 
@@ -1142,7 +848,7 @@ static void __init __efi_enter_virtual_mode(void)
        efi.runtime_version = efi_systab.hdr.revision;
 
        if (efi_is_native())
-               native_runtime_setup();
+               efi_native_runtime_setup();
        else
                efi_thunk_runtime_setup();
 
@@ -1179,15 +885,14 @@ 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)
 {
+       if (efi_enabled(EFI_PARAVIRT))
+               return;
+
        if (efi_setup)
                kexec_enter_virtual_mode();
        else
@@ -1220,6 +925,9 @@ u64 efi_mem_attributes(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) &&
@@ -1230,86 +938,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 +949,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..1c7380d
--- /dev/null
@@ -0,0 +1,290 @@
+#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 <linux/acpi.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_memunmap(tablep, nr_tables * sz);
+
+out_memremap:
+       early_memunmap(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);
+}
+
+/*
+ * For most modern platforms the preferred method of powering off is via
+ * ACPI. However, there are some that are known to require the use of
+ * EFI runtime services and for which ACPI does not work at all.
+ *
+ * Using EFI is a last resort, to be used only if no other option
+ * exists.
+ */
+bool efi_reboot_required(void)
+{
+       if (!acpi_gbl_reduced_hardware)
+               return false;
+
+       efi_reboot_quirk_mode = EFI_RESET_WARM;
+       return true;
+}
+
+bool efi_poweroff_required(void)
+{
+       return !!acpi_gbl_reduced_hardware;
+}
index 96ab2c0..7322755 100644 (file)
@@ -22,3 +22,4 @@ obj-$(CONFIG_PARAVIRT_SPINLOCKS)+= spinlock.o
 obj-$(CONFIG_XEN_DEBUG_FS)     += debugfs.o
 obj-$(CONFIG_XEN_DOM0)         += apic.o vga.o
 obj-$(CONFIG_SWIOTLB_XEN)      += pci-swiotlb-xen.o
+obj-$(CONFIG_XEN_EFI)          += efi.o
diff --git a/arch/x86/xen/efi.c b/arch/x86/xen/efi.c
new file mode 100644 (file)
index 0000000..a02e09e
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2014 Oracle Co., Daniel Kiper
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/efi.h>
+#include <linux/init.h>
+#include <linux/string.h>
+
+#include <xen/xen-ops.h>
+
+#include <asm/setup.h>
+
+void __init xen_efi_init(void)
+{
+       efi_system_table_t *efi_systab_xen;
+
+       efi_systab_xen = xen_efi_probe();
+
+       if (efi_systab_xen == NULL)
+               return;
+
+       strncpy((char *)&boot_params.efi_info.efi_loader_signature, "Xen",
+                       sizeof(boot_params.efi_info.efi_loader_signature));
+       boot_params.efi_info.efi_systab = (__u32)__pa(efi_systab_xen);
+       boot_params.efi_info.efi_systab_hi = (__u32)(__pa(efi_systab_xen) >> 32);
+
+       set_bit(EFI_BOOT, &efi.flags);
+       set_bit(EFI_PARAVIRT, &efi.flags);
+       set_bit(EFI_64BIT, &efi.flags);
+}
index ffb101e..9481351 100644 (file)
@@ -1718,6 +1718,8 @@ asmlinkage __visible void __init xen_start_kernel(void)
 
        xen_setup_runstate_info(0);
 
+       xen_efi_init();
+
        /* Start the world */
 #ifdef CONFIG_X86_32
        i386_start_kernel();
index 97d8765..28c7e0b 100644 (file)
@@ -105,6 +105,14 @@ static inline void __init xen_init_apic(void)
 }
 #endif
 
+#ifdef CONFIG_XEN_EFI
+extern void xen_efi_init(void);
+#else
+static inline void __init xen_efi_init(void)
+{
+}
+#endif
+
 /* Declare an asm function, along with symbols needed to make it
    inlineable */
 #define DECL_ASM(ret, name, ...)               \
index d420ae2..f712d47 100644 (file)
@@ -54,6 +54,12 @@ config EFI_PARAMS_FROM_FDT
          the EFI runtime support gets system table address, memory
           map address, and other parameters from the device tree.
 
+config EFI_RUNTIME_WRAPPERS
+       bool
+
+config EFI_ARMSTUB
+       bool
+
 endmenu
 
 config UEFI_CPER
index 9553496..d8be608 100644 (file)
@@ -1,8 +1,10 @@
 #
 # Makefile for linux kernel
 #
-obj-$(CONFIG_EFI)                      += efi.o vars.o
+obj-$(CONFIG_EFI)                      += efi.o vars.o reboot.o
 obj-$(CONFIG_EFI_VARS)                 += efivars.o
 obj-$(CONFIG_EFI_VARS_PSTORE)          += efi-pstore.o
 obj-$(CONFIG_UEFI_CPER)                        += cper.o
 obj-$(CONFIG_EFI_RUNTIME_MAP)          += runtime-map.o
+obj-$(CONFIG_EFI_RUNTIME_WRAPPERS)     += runtime-wrappers.o
+obj-$(CONFIG_EFI_STUB)                 += libstub/
index dc79346..64ecbb5 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/of.h>
 #include <linux/of_fdt.h>
 #include <linux/io.h>
+#include <linux/platform_device.h>
 
 struct efi __read_mostly efi = {
        .mps        = EFI_INVALID_TABLE_ADDR,
@@ -104,16 +105,19 @@ static struct attribute *efi_subsys_attrs[] = {
 static umode_t efi_attr_is_visible(struct kobject *kobj,
                                   struct attribute *attr, int n)
 {
-       umode_t mode = attr->mode;
-
-       if (attr == &efi_attr_fw_vendor.attr)
-               return (efi.fw_vendor == EFI_INVALID_TABLE_ADDR) ? 0 : mode;
-       else if (attr == &efi_attr_runtime.attr)
-               return (efi.runtime == EFI_INVALID_TABLE_ADDR) ? 0 : mode;
-       else if (attr == &efi_attr_config_table.attr)
-               return (efi.config_table == EFI_INVALID_TABLE_ADDR) ? 0 : mode;
+       if (attr == &efi_attr_fw_vendor.attr) {
+               if (efi_enabled(EFI_PARAVIRT) ||
+                               efi.fw_vendor == EFI_INVALID_TABLE_ADDR)
+                       return 0;
+       } else if (attr == &efi_attr_runtime.attr) {
+               if (efi.runtime == EFI_INVALID_TABLE_ADDR)
+                       return 0;
+       } else if (attr == &efi_attr_config_table.attr) {
+               if (efi.config_table == EFI_INVALID_TABLE_ADDR)
+                       return 0;
+       }
 
-       return mode;
+       return attr->mode;
 }
 
 static struct attribute_group efi_subsys_attr_group = {
@@ -298,7 +302,7 @@ int __init efi_config_init(efi_config_table_type_t *arch_tables)
                        if (table64 >> 32) {
                                pr_cont("\n");
                                pr_err("Table located above 4GB, disabling EFI.\n");
-                               early_iounmap(config_tables,
+                               early_memunmap(config_tables,
                                               efi.systab->nr_tables * sz);
                                return -EINVAL;
                        }
@@ -314,13 +318,27 @@ int __init efi_config_init(efi_config_table_type_t *arch_tables)
                tablep += sz;
        }
        pr_cont("\n");
-       early_iounmap(config_tables, efi.systab->nr_tables * sz);
+       early_memunmap(config_tables, efi.systab->nr_tables * sz);
 
        set_bit(EFI_CONFIG_TABLES, &efi.flags);
 
        return 0;
 }
 
+#ifdef CONFIG_EFI_VARS_MODULE
+static int __init efi_load_efivars(void)
+{
+       struct platform_device *pdev;
+
+       if (!efi_enabled(EFI_RUNTIME_SERVICES))
+               return 0;
+
+       pdev = platform_device_register_simple("efivars", 0, NULL, 0);
+       return IS_ERR(pdev) ? PTR_ERR(pdev) : 0;
+}
+device_initcall(efi_load_efivars);
+#endif
+
 #ifdef CONFIG_EFI_PARAMS_FROM_FDT
 
 #define UEFI_PARAM(name, prop, field)                     \
index 463c565..f256ecd 100644 (file)
@@ -78,6 +78,7 @@ MODULE_AUTHOR("Matt Domsch <Matt_Domsch@Dell.com>");
 MODULE_DESCRIPTION("sysfs interface to EFI Variables");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(EFIVARS_VERSION);
+MODULE_ALIAS("platform:efivars");
 
 LIST_HEAD(efivar_sysfs_list);
 EXPORT_SYMBOL_GPL(efivar_sysfs_list);
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
new file mode 100644 (file)
index 0000000..b14bc2b
--- /dev/null
@@ -0,0 +1,26 @@
+#
+# The stub may be linked into the kernel proper or into a separate boot binary,
+# but in either case, it executes before the kernel does (with MMU disabled) so
+# things like ftrace and stack-protector are likely to cause trouble if left
+# enabled, even if doing so doesn't break the build.
+#
+cflags-$(CONFIG_X86_32)                := -march=i386
+cflags-$(CONFIG_X86_64)                := -mcmodel=small
+cflags-$(CONFIG_X86)           += -m$(BITS) -D__KERNEL__ $(LINUX_INCLUDE) -O2 \
+                                  -fPIC -fno-strict-aliasing -mno-red-zone \
+                                  -mno-mmx -mno-sse -DDISABLE_BRANCH_PROFILING
+
+cflags-$(CONFIG_ARM64)         := $(subst -pg,,$(KBUILD_CFLAGS))
+cflags-$(CONFIG_ARM)           := $(subst -pg,,$(KBUILD_CFLAGS)) \
+                                  -fno-builtin -fpic -mno-single-pic-base
+
+KBUILD_CFLAGS                  := $(cflags-y) \
+                                  $(call cc-option,-ffreestanding) \
+                                  $(call cc-option,-fno-stack-protector)
+
+GCOV_PROFILE                   := n
+
+lib-y                          := efi-stub-helper.o
+lib-$(CONFIG_EFI_ARMSTUB)      += arm-stub.o fdt.o
+
+CFLAGS_fdt.o                   += -I$(srctree)/scripts/dtc/libfdt/
similarity index 93%
rename from drivers/firmware/efi/arm-stub.c
rename to drivers/firmware/efi/libstub/arm-stub.c
index 41114ce..480339b 100644 (file)
  *
  */
 
+#include <linux/efi.h>
+#include <asm/efi.h>
+
+#include "efistub.h"
+
 static int __init efi_secureboot_enabled(efi_system_table_t *sys_table_arg)
 {
        static efi_guid_t const var_guid __initconst = EFI_GLOBAL_VARIABLE_GUID;
@@ -36,8 +41,8 @@ static int __init efi_secureboot_enabled(efi_system_table_t *sys_table_arg)
        }
 }
 
-static efi_status_t efi_open_volume(efi_system_table_t *sys_table_arg,
-                                   void *__image, void **__fh)
+efi_status_t efi_open_volume(efi_system_table_t *sys_table_arg,
+                            void *__image, void **__fh)
 {
        efi_file_io_interface_t *io;
        efi_loaded_image_t *image = __image;
@@ -60,14 +65,15 @@ static efi_status_t efi_open_volume(efi_system_table_t *sys_table_arg,
        *__fh = fh;
        return status;
 }
-static efi_status_t efi_file_close(void *handle)
+
+efi_status_t efi_file_close(void *handle)
 {
        efi_file_handle_t *fh = handle;
 
        return fh->close(handle);
 }
 
-static efi_status_t
+efi_status_t
 efi_file_read(void *handle, unsigned long *size, void *addr)
 {
        efi_file_handle_t *fh = handle;
@@ -76,7 +82,7 @@ efi_file_read(void *handle, unsigned long *size, void *addr)
 }
 
 
-static efi_status_t
+efi_status_t
 efi_file_size(efi_system_table_t *sys_table_arg, void *__fh,
              efi_char16_t *filename_16, void **handle, u64 *file_sz)
 {
@@ -129,7 +135,7 @@ grow:
 
 
 
-static void efi_char16_printk(efi_system_table_t *sys_table_arg,
+void efi_char16_printk(efi_system_table_t *sys_table_arg,
                              efi_char16_t *str)
 {
        struct efi_simple_text_output_protocol *out;
@@ -145,13 +151,13 @@ static void efi_char16_printk(efi_system_table_t *sys_table_arg,
  * must be reserved. On failure it is required to free all
  * all allocations it has made.
  */
-static efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
-                                       unsigned long *image_addr,
-                                       unsigned long *image_size,
-                                       unsigned long *reserve_addr,
-                                       unsigned long *reserve_size,
-                                       unsigned long dram_base,
-                                       efi_loaded_image_t *image);
+efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
+                                unsigned long *image_addr,
+                                unsigned long *image_size,
+                                unsigned long *reserve_addr,
+                                unsigned long *reserve_size,
+                                unsigned long dram_base,
+                                efi_loaded_image_t *image);
 /*
  * EFI entry point for the arm/arm64 EFI stubs.  This is the entrypoint
  * that is described in the PE/COFF header.  Most of the code is the same
similarity index 88%
rename from drivers/firmware/efi/efi-stub-helper.c
rename to drivers/firmware/efi/libstub/efi-stub-helper.c
index eb6d4be..32d5cca 100644 (file)
@@ -9,18 +9,20 @@
  * under the terms of the GNU General Public License version 2.
  *
  */
-#define EFI_READ_CHUNK_SIZE    (1024 * 1024)
 
-/* error code which can't be mistaken for valid address */
-#define EFI_ERROR      (~0UL)
+#include <linux/efi.h>
+#include <asm/efi.h>
+
+#include "efistub.h"
 
+#define EFI_READ_CHUNK_SIZE    (1024 * 1024)
 
 struct file_info {
        efi_file_handle_t *handle;
        u64 size;
 };
 
-static void efi_printk(efi_system_table_t *sys_table_arg, char *str)
+void efi_printk(efi_system_table_t *sys_table_arg, char *str)
 {
        char *s8;
 
@@ -37,16 +39,12 @@ static void efi_printk(efi_system_table_t *sys_table_arg, char *str)
        }
 }
 
-#define pr_efi(sys_table, msg)     efi_printk(sys_table, "EFI stub: "msg)
-#define pr_efi_err(sys_table, msg) efi_printk(sys_table, "EFI stub: ERROR: "msg)
-
-
-static efi_status_t efi_get_memory_map(efi_system_table_t *sys_table_arg,
-                                      efi_memory_desc_t **map,
-                                      unsigned long *map_size,
-                                      unsigned long *desc_size,
-                                      u32 *desc_ver,
-                                      unsigned long *key_ptr)
+efi_status_t efi_get_memory_map(efi_system_table_t *sys_table_arg,
+                               efi_memory_desc_t **map,
+                               unsigned long *map_size,
+                               unsigned long *desc_size,
+                               u32 *desc_ver,
+                               unsigned long *key_ptr)
 {
        efi_memory_desc_t *m = NULL;
        efi_status_t status;
@@ -88,7 +86,7 @@ fail:
 }
 
 
-static unsigned long __init get_dram_base(efi_system_table_t *sys_table_arg)
+unsigned long __init get_dram_base(efi_system_table_t *sys_table_arg)
 {
        efi_status_t status;
        unsigned long map_size;
@@ -116,9 +114,9 @@ static unsigned long __init get_dram_base(efi_system_table_t *sys_table_arg)
 /*
  * Allocate at the highest possible address that is not above 'max'.
  */
-static efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg,
-                              unsigned long size, unsigned long align,
-                              unsigned long *addr, unsigned long max)
+efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg,
+                           unsigned long size, unsigned long align,
+                           unsigned long *addr, unsigned long max)
 {
        unsigned long map_size, desc_size;
        efi_memory_desc_t *map;
@@ -202,9 +200,9 @@ fail:
 /*
  * Allocate at the lowest possible address.
  */
-static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
-                             unsigned long size, unsigned long align,
-                             unsigned long *addr)
+efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
+                          unsigned long size, unsigned long align,
+                          unsigned long *addr)
 {
        unsigned long map_size, desc_size;
        efi_memory_desc_t *map;
@@ -271,8 +269,8 @@ fail:
        return status;
 }
 
-static void efi_free(efi_system_table_t *sys_table_arg, unsigned long size,
-                    unsigned long addr)
+void efi_free(efi_system_table_t *sys_table_arg, unsigned long size,
+             unsigned long addr)
 {
        unsigned long nr_pages;
 
@@ -290,12 +288,12 @@ static void efi_free(efi_system_table_t *sys_table_arg, unsigned long size,
  * We only support loading a file from the same filesystem as
  * the kernel image.
  */
-static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
-                                        efi_loaded_image_t *image,
-                                        char *cmd_line, char *option_string,
-                                        unsigned long max_addr,
-                                        unsigned long *load_addr,
-                                        unsigned long *load_size)
+efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
+                                 efi_loaded_image_t *image,
+                                 char *cmd_line, char *option_string,
+                                 unsigned long max_addr,
+                                 unsigned long *load_addr,
+                                 unsigned long *load_size)
 {
        struct file_info *files;
        unsigned long file_addr;
@@ -477,12 +475,12 @@ fail:
  * address is not available the lowest available address will
  * be used.
  */
-static efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg,
-                                       unsigned long *image_addr,
-                                       unsigned long image_size,
-                                       unsigned long alloc_size,
-                                       unsigned long preferred_addr,
-                                       unsigned long alignment)
+efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg,
+                                unsigned long *image_addr,
+                                unsigned long image_size,
+                                unsigned long alloc_size,
+                                unsigned long preferred_addr,
+                                unsigned long alignment)
 {
        unsigned long cur_image_addr;
        unsigned long new_addr = 0;
@@ -589,9 +587,9 @@ static u8 *efi_utf16_to_utf8(u8 *dst, const u16 *src, int n)
  * Size of memory allocated return in *cmd_line_len.
  * Returns NULL on error.
  */
-static char *efi_convert_cmdline(efi_system_table_t *sys_table_arg,
-                                efi_loaded_image_t *image,
-                                int *cmd_line_len)
+char *efi_convert_cmdline(efi_system_table_t *sys_table_arg,
+                         efi_loaded_image_t *image,
+                         int *cmd_line_len)
 {
        const u16 *s2;
        u8 *s1 = NULL;
diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
new file mode 100644 (file)
index 0000000..304ab29
--- /dev/null
@@ -0,0 +1,42 @@
+
+#ifndef _DRIVERS_FIRMWARE_EFI_EFISTUB_H
+#define _DRIVERS_FIRMWARE_EFI_EFISTUB_H
+
+/* error code which can't be mistaken for valid address */
+#define EFI_ERROR      (~0UL)
+
+void efi_char16_printk(efi_system_table_t *, efi_char16_t *);
+
+efi_status_t efi_open_volume(efi_system_table_t *sys_table_arg, void *__image,
+                            void **__fh);
+
+efi_status_t efi_file_size(efi_system_table_t *sys_table_arg, void *__fh,
+                          efi_char16_t *filename_16, void **handle,
+                          u64 *file_sz);
+
+efi_status_t efi_file_read(void *handle, unsigned long *size, void *addr);
+
+efi_status_t efi_file_close(void *handle);
+
+unsigned long get_dram_base(efi_system_table_t *sys_table_arg);
+
+efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
+                       unsigned long orig_fdt_size,
+                       void *fdt, int new_fdt_size, char *cmdline_ptr,
+                       u64 initrd_addr, u64 initrd_size,
+                       efi_memory_desc_t *memory_map,
+                       unsigned long map_size, unsigned long desc_size,
+                       u32 desc_ver);
+
+efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
+                                           void *handle,
+                                           unsigned long *new_fdt_addr,
+                                           unsigned long max_addr,
+                                           u64 initrd_addr, u64 initrd_size,
+                                           char *cmdline_ptr,
+                                           unsigned long fdt_addr,
+                                           unsigned long fdt_size);
+
+void *get_fdt(efi_system_table_t *sys_table);
+
+#endif
similarity index 94%
rename from drivers/firmware/efi/fdt.c
rename to drivers/firmware/efi/libstub/fdt.c
index 507a3df..a56bb35 100644 (file)
  *
  */
 
-static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
-                              unsigned long orig_fdt_size,
-                              void *fdt, int new_fdt_size, char *cmdline_ptr,
-                              u64 initrd_addr, u64 initrd_size,
-                              efi_memory_desc_t *memory_map,
-                              unsigned long map_size, unsigned long desc_size,
-                              u32 desc_ver)
+#include <linux/efi.h>
+#include <linux/libfdt.h>
+#include <asm/efi.h>
+
+efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
+                       unsigned long orig_fdt_size,
+                       void *fdt, int new_fdt_size, char *cmdline_ptr,
+                       u64 initrd_addr, u64 initrd_size,
+                       efi_memory_desc_t *memory_map,
+                       unsigned long map_size, unsigned long desc_size,
+                       u32 desc_ver)
 {
        int node, prev;
        int status;
@@ -255,7 +259,7 @@ fail:
        return EFI_LOAD_ERROR;
 }
 
-static void *get_fdt(efi_system_table_t *sys_table)
+void *get_fdt(efi_system_table_t *sys_table)
 {
        efi_guid_t fdt_guid = DEVICE_TREE_GUID;
        efi_config_table_t *tables;
diff --git a/drivers/firmware/efi/reboot.c b/drivers/firmware/efi/reboot.c
new file mode 100644 (file)
index 0000000..9c59d1c
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 Intel Corporation; author Matt Fleming
+ * Copyright (c) 2014 Red Hat, Inc., Mark Salter <msalter@redhat.com>
+ */
+#include <linux/efi.h>
+#include <linux/reboot.h>
+
+int efi_reboot_quirk_mode = -1;
+
+void efi_reboot(enum reboot_mode reboot_mode, const char *__unused)
+{
+       int efi_mode;
+
+       if (!efi_enabled(EFI_RUNTIME_SERVICES))
+               return;
+
+       switch (reboot_mode) {
+       case REBOOT_WARM:
+       case REBOOT_SOFT:
+               efi_mode = EFI_RESET_WARM;
+               break;
+       default:
+               efi_mode = EFI_RESET_COLD;
+               break;
+       }
+
+       /*
+        * If a quirk forced an EFI reset mode, always use that.
+        */
+       if (efi_reboot_quirk_mode != -1)
+               efi_mode = efi_reboot_quirk_mode;
+
+       efi.reset_system(efi_mode, EFI_SUCCESS, 0, NULL);
+}
+
+bool __weak efi_poweroff_required(void)
+{
+       return false;
+}
+
+static void efi_power_off(void)
+{
+       efi.reset_system(EFI_RESET_SHUTDOWN, EFI_SUCCESS, 0, NULL);
+}
+
+static int __init efi_shutdown_init(void)
+{
+       if (!efi_enabled(EFI_RUNTIME_SERVICES))
+               return -ENODEV;
+
+       if (efi_poweroff_required())
+               pm_power_off = efi_power_off;
+
+       return 0;
+}
+late_initcall(efi_shutdown_init);
diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c
new file mode 100644 (file)
index 0000000..10daa4b
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * runtime-wrappers.c - Runtime Services function call wrappers
+ *
+ * Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org>
+ *
+ * Split off from arch/x86/platform/efi/efi.c
+ *
+ * Copyright (C) 1999 VA Linux Systems
+ * Copyright (C) 1999 Walt Drummond <drummond@valinux.com>
+ * Copyright (C) 1999-2002 Hewlett-Packard Co.
+ * Copyright (C) 2005-2008 Intel Co.
+ * Copyright (C) 2013 SuSE Labs
+ *
+ * This file is released under the GPLv2.
+ */
+
+#include <linux/efi.h>
+#include <linux/spinlock.h>             /* spinlock_t */
+#include <asm/efi.h>
+
+/*
+ * As per commit ef68c8f87ed1 ("x86: Serialize EFI time accesses on rtc_lock"),
+ * the EFI specification requires that callers of the time related runtime
+ * functions serialize with other CMOS accesses in the kernel, as the EFI time
+ * functions may choose to also use the legacy CMOS RTC.
+ */
+__weak DEFINE_SPINLOCK(rtc_lock);
+
+static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc)
+{
+       unsigned long flags;
+       efi_status_t status;
+
+       spin_lock_irqsave(&rtc_lock, flags);
+       status = efi_call_virt(get_time, tm, tc);
+       spin_unlock_irqrestore(&rtc_lock, flags);
+       return status;
+}
+
+static efi_status_t virt_efi_set_time(efi_time_t *tm)
+{
+       unsigned long flags;
+       efi_status_t status;
+
+       spin_lock_irqsave(&rtc_lock, flags);
+       status = efi_call_virt(set_time, tm);
+       spin_unlock_irqrestore(&rtc_lock, flags);
+       return status;
+}
+
+static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled,
+                                            efi_bool_t *pending,
+                                            efi_time_t *tm)
+{
+       unsigned long flags;
+       efi_status_t status;
+
+       spin_lock_irqsave(&rtc_lock, flags);
+       status = efi_call_virt(get_wakeup_time, enabled, pending, tm);
+       spin_unlock_irqrestore(&rtc_lock, flags);
+       return status;
+}
+
+static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm)
+{
+       unsigned long flags;
+       efi_status_t status;
+
+       spin_lock_irqsave(&rtc_lock, flags);
+       status = efi_call_virt(set_wakeup_time, enabled, tm);
+       spin_unlock_irqrestore(&rtc_lock, flags);
+       return status;
+}
+
+static efi_status_t virt_efi_get_variable(efi_char16_t *name,
+                                         efi_guid_t *vendor,
+                                         u32 *attr,
+                                         unsigned long *data_size,
+                                         void *data)
+{
+       return efi_call_virt(get_variable, name, vendor, attr, data_size, data);
+}
+
+static efi_status_t virt_efi_get_next_variable(unsigned long *name_size,
+                                              efi_char16_t *name,
+                                              efi_guid_t *vendor)
+{
+       return efi_call_virt(get_next_variable, name_size, name, vendor);
+}
+
+static efi_status_t virt_efi_set_variable(efi_char16_t *name,
+                                         efi_guid_t *vendor,
+                                         u32 attr,
+                                         unsigned long data_size,
+                                         void *data)
+{
+       return efi_call_virt(set_variable, name, vendor, attr, data_size, data);
+}
+
+static efi_status_t virt_efi_query_variable_info(u32 attr,
+                                                u64 *storage_space,
+                                                u64 *remaining_space,
+                                                u64 *max_variable_size)
+{
+       if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
+               return EFI_UNSUPPORTED;
+
+       return efi_call_virt(query_variable_info, attr, storage_space,
+                            remaining_space, max_variable_size);
+}
+
+static efi_status_t virt_efi_get_next_high_mono_count(u32 *count)
+{
+       return efi_call_virt(get_next_high_mono_count, count);
+}
+
+static void virt_efi_reset_system(int reset_type,
+                                 efi_status_t status,
+                                 unsigned long data_size,
+                                 efi_char16_t *data)
+{
+       __efi_call_virt(reset_system, reset_type, status, data_size, data);
+}
+
+static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules,
+                                           unsigned long count,
+                                           unsigned long sg_list)
+{
+       if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
+               return EFI_UNSUPPORTED;
+
+       return efi_call_virt(update_capsule, capsules, count, sg_list);
+}
+
+static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules,
+                                               unsigned long count,
+                                               u64 *max_size,
+                                               int *reset_type)
+{
+       if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
+               return EFI_UNSUPPORTED;
+
+       return efi_call_virt(query_capsule_caps, capsules, count, max_size,
+                            reset_type);
+}
+
+void efi_native_runtime_setup(void)
+{
+       efi.get_time = virt_efi_get_time;
+       efi.set_time = virt_efi_set_time;
+       efi.get_wakeup_time = virt_efi_get_wakeup_time;
+       efi.set_wakeup_time = virt_efi_set_wakeup_time;
+       efi.get_variable = virt_efi_get_variable;
+       efi.get_next_variable = virt_efi_get_next_variable;
+       efi.set_variable = virt_efi_set_variable;
+       efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count;
+       efi.reset_system = virt_efi_reset_system;
+       efi.query_variable_info = virt_efi_query_variable_info;
+       efi.update_capsule = virt_efi_update_capsule;
+       efi.query_capsule_caps = virt_efi_query_capsule_caps;
+}
index 38fb36e..8bc0183 100644 (file)
@@ -240,4 +240,8 @@ config XEN_MCE_LOG
 config XEN_HAVE_PVMMU
        bool
 
+config XEN_EFI
+       def_bool y
+       depends on X86_64 && EFI
+
 endmenu
index 45e00af..84044b5 100644 (file)
@@ -9,6 +9,8 @@ obj-y   += xenbus/
 nostackp := $(call cc-option, -fno-stack-protector)
 CFLAGS_features.o                      := $(nostackp)
 
+CFLAGS_efi.o                           += -fshort-wchar
+
 dom0-$(CONFIG_PCI) += pci.o
 dom0-$(CONFIG_USB_SUPPORT) += dbgp.o
 dom0-$(CONFIG_ACPI) += acpi.o $(xen-pad-y)
@@ -33,6 +35,7 @@ obj-$(CONFIG_XEN_STUB)                        += xen-stub.o
 obj-$(CONFIG_XEN_ACPI_HOTPLUG_MEMORY)  += xen-acpi-memhotplug.o
 obj-$(CONFIG_XEN_ACPI_HOTPLUG_CPU)     += xen-acpi-cpuhotplug.o
 obj-$(CONFIG_XEN_ACPI_PROCESSOR)       += xen-acpi-processor.o
+obj-$(CONFIG_XEN_EFI)                  += efi.o
 xen-evtchn-y                           := evtchn.o
 xen-gntdev-y                           := gntdev.o
 xen-gntalloc-y                         := gntalloc.o
diff --git a/drivers/xen/efi.c b/drivers/xen/efi.c
new file mode 100644 (file)
index 0000000..31f618a
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ * EFI support for Xen.
+ *
+ * Copyright (C) 1999 VA Linux Systems
+ * Copyright (C) 1999 Walt Drummond <drummond@valinux.com>
+ * Copyright (C) 1999-2002 Hewlett-Packard Co.
+ *     David Mosberger-Tang <davidm@hpl.hp.com>
+ *     Stephane Eranian <eranian@hpl.hp.com>
+ * Copyright (C) 2005-2008 Intel Co.
+ *     Fenghua Yu <fenghua.yu@intel.com>
+ *     Bibo Mao <bibo.mao@intel.com>
+ *     Chandramouli Narayanan <mouli@linux.intel.com>
+ *     Huang Ying <ying.huang@intel.com>
+ * Copyright (C) 2011 Novell Co.
+ *     Jan Beulich <JBeulich@suse.com>
+ * Copyright (C) 2011-2012 Oracle Co.
+ *     Liang Tang <liang.tang@oracle.com>
+ * Copyright (c) 2014 Oracle Co., Daniel Kiper
+ */
+
+#include <linux/bug.h>
+#include <linux/efi.h>
+#include <linux/init.h>
+#include <linux/string.h>
+
+#include <xen/interface/xen.h>
+#include <xen/interface/platform.h>
+#include <xen/xen.h>
+
+#include <asm/xen/hypercall.h>
+
+#define INIT_EFI_OP(name) \
+       {.cmd = XENPF_efi_runtime_call, \
+        .u.efi_runtime_call.function = XEN_EFI_##name, \
+        .u.efi_runtime_call.misc = 0}
+
+#define efi_data(op)   (op.u.efi_runtime_call)
+
+static efi_status_t xen_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc)
+{
+       struct xen_platform_op op = INIT_EFI_OP(get_time);
+
+       if (HYPERVISOR_dom0_op(&op) < 0)
+               return EFI_UNSUPPORTED;
+
+       if (tm) {
+               BUILD_BUG_ON(sizeof(*tm) != sizeof(efi_data(op).u.get_time.time));
+               memcpy(tm, &efi_data(op).u.get_time.time, sizeof(*tm));
+       }
+
+       if (tc) {
+               tc->resolution = efi_data(op).u.get_time.resolution;
+               tc->accuracy = efi_data(op).u.get_time.accuracy;
+               tc->sets_to_zero = !!(efi_data(op).misc &
+                                     XEN_EFI_GET_TIME_SET_CLEARS_NS);
+       }
+
+       return efi_data(op).status;
+}
+
+static efi_status_t xen_efi_set_time(efi_time_t *tm)
+{
+       struct xen_platform_op op = INIT_EFI_OP(set_time);
+
+       BUILD_BUG_ON(sizeof(*tm) != sizeof(efi_data(op).u.set_time));
+       memcpy(&efi_data(op).u.set_time, tm, sizeof(*tm));
+
+       if (HYPERVISOR_dom0_op(&op) < 0)
+               return EFI_UNSUPPORTED;
+
+       return efi_data(op).status;
+}
+
+static efi_status_t xen_efi_get_wakeup_time(efi_bool_t *enabled,
+                                           efi_bool_t *pending,
+                                           efi_time_t *tm)
+{
+       struct xen_platform_op op = INIT_EFI_OP(get_wakeup_time);
+
+       if (HYPERVISOR_dom0_op(&op) < 0)
+               return EFI_UNSUPPORTED;
+
+       if (tm) {
+               BUILD_BUG_ON(sizeof(*tm) != sizeof(efi_data(op).u.get_wakeup_time));
+               memcpy(tm, &efi_data(op).u.get_wakeup_time, sizeof(*tm));
+       }
+
+       if (enabled)
+               *enabled = !!(efi_data(op).misc & XEN_EFI_GET_WAKEUP_TIME_ENABLED);
+
+       if (pending)
+               *pending = !!(efi_data(op).misc & XEN_EFI_GET_WAKEUP_TIME_PENDING);
+
+       return efi_data(op).status;
+}
+
+static efi_status_t xen_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm)
+{
+       struct xen_platform_op op = INIT_EFI_OP(set_wakeup_time);
+
+       BUILD_BUG_ON(sizeof(*tm) != sizeof(efi_data(op).u.set_wakeup_time));
+       if (enabled)
+               efi_data(op).misc = XEN_EFI_SET_WAKEUP_TIME_ENABLE;
+       if (tm)
+               memcpy(&efi_data(op).u.set_wakeup_time, tm, sizeof(*tm));
+       else
+               efi_data(op).misc |= XEN_EFI_SET_WAKEUP_TIME_ENABLE_ONLY;
+
+       if (HYPERVISOR_dom0_op(&op) < 0)
+               return EFI_UNSUPPORTED;
+
+       return efi_data(op).status;
+}
+
+static efi_status_t xen_efi_get_variable(efi_char16_t *name,
+                                        efi_guid_t *vendor,
+                                        u32 *attr,
+                                        unsigned long *data_size,
+                                        void *data)
+{
+       struct xen_platform_op op = INIT_EFI_OP(get_variable);
+
+       set_xen_guest_handle(efi_data(op).u.get_variable.name, name);
+       BUILD_BUG_ON(sizeof(*vendor) !=
+                    sizeof(efi_data(op).u.get_variable.vendor_guid));
+       memcpy(&efi_data(op).u.get_variable.vendor_guid, vendor, sizeof(*vendor));
+       efi_data(op).u.get_variable.size = *data_size;
+       set_xen_guest_handle(efi_data(op).u.get_variable.data, data);
+
+       if (HYPERVISOR_dom0_op(&op) < 0)
+               return EFI_UNSUPPORTED;
+
+       *data_size = efi_data(op).u.get_variable.size;
+       if (attr)
+               *attr = efi_data(op).misc;
+
+       return efi_data(op).status;
+}
+
+static efi_status_t xen_efi_get_next_variable(unsigned long *name_size,
+                                             efi_char16_t *name,
+                                             efi_guid_t *vendor)
+{
+       struct xen_platform_op op = INIT_EFI_OP(get_next_variable_name);
+
+       efi_data(op).u.get_next_variable_name.size = *name_size;
+       set_xen_guest_handle(efi_data(op).u.get_next_variable_name.name, name);
+       BUILD_BUG_ON(sizeof(*vendor) !=
+                    sizeof(efi_data(op).u.get_next_variable_name.vendor_guid));
+       memcpy(&efi_data(op).u.get_next_variable_name.vendor_guid, vendor,
+              sizeof(*vendor));
+
+       if (HYPERVISOR_dom0_op(&op) < 0)
+               return EFI_UNSUPPORTED;
+
+       *name_size = efi_data(op).u.get_next_variable_name.size;
+       memcpy(vendor, &efi_data(op).u.get_next_variable_name.vendor_guid,
+              sizeof(*vendor));
+
+       return efi_data(op).status;
+}
+
+static efi_status_t xen_efi_set_variable(efi_char16_t *name,
+                                        efi_guid_t *vendor,
+                                        u32 attr,
+                                        unsigned long data_size,
+                                        void *data)
+{
+       struct xen_platform_op op = INIT_EFI_OP(set_variable);
+
+       set_xen_guest_handle(efi_data(op).u.set_variable.name, name);
+       efi_data(op).misc = attr;
+       BUILD_BUG_ON(sizeof(*vendor) !=
+                    sizeof(efi_data(op).u.set_variable.vendor_guid));
+       memcpy(&efi_data(op).u.set_variable.vendor_guid, vendor, sizeof(*vendor));
+       efi_data(op).u.set_variable.size = data_size;
+       set_xen_guest_handle(efi_data(op).u.set_variable.data, data);
+
+       if (HYPERVISOR_dom0_op(&op) < 0)
+               return EFI_UNSUPPORTED;
+
+       return efi_data(op).status;
+}
+
+static efi_status_t xen_efi_query_variable_info(u32 attr,
+                                               u64 *storage_space,
+                                               u64 *remaining_space,
+                                               u64 *max_variable_size)
+{
+       struct xen_platform_op op = INIT_EFI_OP(query_variable_info);
+
+       if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
+               return EFI_UNSUPPORTED;
+
+       efi_data(op).u.query_variable_info.attr = attr;
+
+       if (HYPERVISOR_dom0_op(&op) < 0)
+               return EFI_UNSUPPORTED;
+
+       *storage_space = efi_data(op).u.query_variable_info.max_store_size;
+       *remaining_space = efi_data(op).u.query_variable_info.remain_store_size;
+       *max_variable_size = efi_data(op).u.query_variable_info.max_size;
+
+       return efi_data(op).status;
+}
+
+static efi_status_t xen_efi_get_next_high_mono_count(u32 *count)
+{
+       struct xen_platform_op op = INIT_EFI_OP(get_next_high_monotonic_count);
+
+       if (HYPERVISOR_dom0_op(&op) < 0)
+               return EFI_UNSUPPORTED;
+
+       *count = efi_data(op).misc;
+
+       return efi_data(op).status;
+}
+
+static efi_status_t xen_efi_update_capsule(efi_capsule_header_t **capsules,
+                                          unsigned long count,
+                                          unsigned long sg_list)
+{
+       struct xen_platform_op op = INIT_EFI_OP(update_capsule);
+
+       if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
+               return EFI_UNSUPPORTED;
+
+       set_xen_guest_handle(efi_data(op).u.update_capsule.capsule_header_array,
+                            capsules);
+       efi_data(op).u.update_capsule.capsule_count = count;
+       efi_data(op).u.update_capsule.sg_list = sg_list;
+
+       if (HYPERVISOR_dom0_op(&op) < 0)
+               return EFI_UNSUPPORTED;
+
+       return efi_data(op).status;
+}
+
+static efi_status_t xen_efi_query_capsule_caps(efi_capsule_header_t **capsules,
+                                              unsigned long count,
+                                              u64 *max_size,
+                                              int *reset_type)
+{
+       struct xen_platform_op op = INIT_EFI_OP(query_capsule_capabilities);
+
+       if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
+               return EFI_UNSUPPORTED;
+
+       set_xen_guest_handle(efi_data(op).u.query_capsule_capabilities.capsule_header_array,
+                                       capsules);
+       efi_data(op).u.query_capsule_capabilities.capsule_count = count;
+
+       if (HYPERVISOR_dom0_op(&op) < 0)
+               return EFI_UNSUPPORTED;
+
+       *max_size = efi_data(op).u.query_capsule_capabilities.max_capsule_size;
+       *reset_type = efi_data(op).u.query_capsule_capabilities.reset_type;
+
+       return efi_data(op).status;
+}
+
+static efi_char16_t vendor[100] __initdata;
+
+static efi_system_table_t efi_systab_xen __initdata = {
+       .hdr = {
+               .signature      = EFI_SYSTEM_TABLE_SIGNATURE,
+               .revision       = 0, /* Initialized later. */
+               .headersize     = 0, /* Ignored by Linux Kernel. */
+               .crc32          = 0, /* Ignored by Linux Kernel. */
+               .reserved       = 0
+       },
+       .fw_vendor      = EFI_INVALID_TABLE_ADDR, /* Initialized later. */
+       .fw_revision    = 0,                      /* Initialized later. */
+       .con_in_handle  = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */
+       .con_in         = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */
+       .con_out_handle = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */
+       .con_out        = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */
+       .stderr_handle  = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */
+       .stderr         = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */
+       .runtime        = (efi_runtime_services_t *)EFI_INVALID_TABLE_ADDR,
+                                                 /* Not used under Xen. */
+       .boottime       = (efi_boot_services_t *)EFI_INVALID_TABLE_ADDR,
+                                                 /* Not used under Xen. */
+       .nr_tables      = 0,                      /* Initialized later. */
+       .tables         = EFI_INVALID_TABLE_ADDR  /* Initialized later. */
+};
+
+static const struct efi efi_xen __initconst = {
+       .systab                   = NULL, /* Initialized later. */
+       .runtime_version          = 0,    /* Initialized later. */
+       .mps                      = EFI_INVALID_TABLE_ADDR,
+       .acpi                     = EFI_INVALID_TABLE_ADDR,
+       .acpi20                   = EFI_INVALID_TABLE_ADDR,
+       .smbios                   = EFI_INVALID_TABLE_ADDR,
+       .sal_systab               = EFI_INVALID_TABLE_ADDR,
+       .boot_info                = EFI_INVALID_TABLE_ADDR,
+       .hcdp                     = EFI_INVALID_TABLE_ADDR,
+       .uga                      = EFI_INVALID_TABLE_ADDR,
+       .uv_systab                = EFI_INVALID_TABLE_ADDR,
+       .fw_vendor                = EFI_INVALID_TABLE_ADDR,
+       .runtime                  = EFI_INVALID_TABLE_ADDR,
+       .config_table             = EFI_INVALID_TABLE_ADDR,
+       .get_time                 = xen_efi_get_time,
+       .set_time                 = xen_efi_set_time,
+       .get_wakeup_time          = xen_efi_get_wakeup_time,
+       .set_wakeup_time          = xen_efi_set_wakeup_time,
+       .get_variable             = xen_efi_get_variable,
+       .get_next_variable        = xen_efi_get_next_variable,
+       .set_variable             = xen_efi_set_variable,
+       .query_variable_info      = xen_efi_query_variable_info,
+       .update_capsule           = xen_efi_update_capsule,
+       .query_capsule_caps       = xen_efi_query_capsule_caps,
+       .get_next_high_mono_count = xen_efi_get_next_high_mono_count,
+       .reset_system             = NULL, /* Functionality provided by Xen. */
+       .set_virtual_address_map  = NULL, /* Not used under Xen. */
+       .memmap                   = NULL, /* Not used under Xen. */
+       .flags                    = 0     /* Initialized later. */
+};
+
+efi_system_table_t __init *xen_efi_probe(void)
+{
+       struct xen_platform_op op = {
+               .cmd = XENPF_firmware_info,
+               .u.firmware_info = {
+                       .type = XEN_FW_EFI_INFO,
+                       .index = XEN_FW_EFI_CONFIG_TABLE
+               }
+       };
+       union xenpf_efi_info *info = &op.u.firmware_info.u.efi_info;
+
+       if (!xen_initial_domain() || HYPERVISOR_dom0_op(&op) < 0)
+               return NULL;
+
+       /* Here we know that Xen runs on EFI platform. */
+
+       efi = efi_xen;
+
+       efi_systab_xen.tables = info->cfg.addr;
+       efi_systab_xen.nr_tables = info->cfg.nent;
+
+       op.cmd = XENPF_firmware_info;
+       op.u.firmware_info.type = XEN_FW_EFI_INFO;
+       op.u.firmware_info.index = XEN_FW_EFI_VENDOR;
+       info->vendor.bufsz = sizeof(vendor);
+       set_xen_guest_handle(info->vendor.name, vendor);
+
+       if (HYPERVISOR_dom0_op(&op) == 0) {
+               efi_systab_xen.fw_vendor = __pa_symbol(vendor);
+               efi_systab_xen.fw_revision = info->vendor.revision;
+       } else
+               efi_systab_xen.fw_vendor = __pa_symbol(L"UNKNOWN");
+
+       op.cmd = XENPF_firmware_info;
+       op.u.firmware_info.type = XEN_FW_EFI_INFO;
+       op.u.firmware_info.index = XEN_FW_EFI_VERSION;
+
+       if (HYPERVISOR_dom0_op(&op) == 0)
+               efi_systab_xen.hdr.revision = info->version;
+
+       op.cmd = XENPF_firmware_info;
+       op.u.firmware_info.type = XEN_FW_EFI_INFO;
+       op.u.firmware_info.index = XEN_FW_EFI_RT_VERSION;
+
+       if (HYPERVISOR_dom0_op(&op) == 0)
+               efi.runtime_version = info->version;
+
+       return &efi_systab_xen;
+}
index 41bbf8b..efc681f 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/ioport.h>
 #include <linux/pfn.h>
 #include <linux/pstore.h>
+#include <linux/reboot.h>
 
 #include <asm/page.h>
 
@@ -521,6 +522,8 @@ typedef efi_status_t efi_query_capsule_caps_t(efi_capsule_header_t **capsules,
                                              int *reset_type);
 typedef efi_status_t efi_query_variable_store_t(u32 attributes, unsigned long size);
 
+void efi_native_runtime_setup(void);
+
 /*
  *  EFI Configuration Table and GUID definitions
  */
@@ -870,11 +873,13 @@ extern int __init efi_uart_console_only (void);
 extern void efi_initialize_iomem_resources(struct resource *code_resource,
                struct resource *data_resource, struct resource *bss_resource);
 extern void efi_get_time(struct timespec *now);
-extern int efi_set_rtc_mmss(const struct timespec *now);
 extern void efi_reserve_boot_services(void);
 extern int efi_get_fdt_params(struct efi_fdt_params *params, int verbose);
 extern struct efi_memory_map memmap;
 
+extern int efi_reboot_quirk_mode;
+extern bool efi_poweroff_required(void);
+
 /* Iterate through an efi_memory_map */
 #define for_each_efi_memory_desc(m, md)                                           \
        for ((md) = (m)->map;                                              \
@@ -916,7 +921,8 @@ extern int __init efi_setup_pcdp_console(char *);
 #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? */
-#define EFI_ARCH_1             6       /* First arch-specific bit */
+#define EFI_PARAVIRT           6       /* Access is via a paravirt interface */
+#define EFI_ARCH_1             7       /* First arch-specific bit */
 
 #ifdef CONFIG_EFI
 /*
@@ -926,11 +932,14 @@ static inline bool efi_enabled(int feature)
 {
        return test_bit(feature, &efi.flags) != 0;
 }
+extern void efi_reboot(enum reboot_mode reboot_mode, const char *__unused);
 #else
 static inline bool efi_enabled(int feature)
 {
        return false;
 }
+static inline void
+efi_reboot(enum reboot_mode reboot_mode, const char *__unused) {}
 #endif
 
 /*
@@ -1031,12 +1040,8 @@ struct efivar_operations {
 struct efivars {
        /*
         * ->lock protects two things:
-        * 1) ->list - adds, removals, reads, writes
-        * 2) ops.[gs]et_variable() calls.
-        * It must not be held when creating sysfs entries or calling kmalloc.
-        * ops.get_next_variable() is only called from register_efivars()
-        * or efivar_update_sysfs_entries(),
-        * which is protected by the BKL, so that path is safe.
+        * 1) efivarfs_list and efivars_sysfs_list
+        * 2) ->ops calls
         */
        spinlock_t lock;
        struct kset *kset;
@@ -1161,4 +1166,46 @@ static inline void
 efi_runtime_map_setup(void *map, int nr_entries, u32 desc_size) {}
 #endif
 
+/* prototypes shared between arch specific and generic stub code */
+
+#define pr_efi(sys_table, msg)     efi_printk(sys_table, "EFI stub: "msg)
+#define pr_efi_err(sys_table, msg) efi_printk(sys_table, "EFI stub: ERROR: "msg)
+
+void efi_printk(efi_system_table_t *sys_table_arg, char *str);
+
+void efi_free(efi_system_table_t *sys_table_arg, unsigned long size,
+             unsigned long addr);
+
+char *efi_convert_cmdline(efi_system_table_t *sys_table_arg,
+                         efi_loaded_image_t *image, int *cmd_line_len);
+
+efi_status_t efi_get_memory_map(efi_system_table_t *sys_table_arg,
+                               efi_memory_desc_t **map,
+                               unsigned long *map_size,
+                               unsigned long *desc_size,
+                               u32 *desc_ver,
+                               unsigned long *key_ptr);
+
+efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,
+                          unsigned long size, unsigned long align,
+                          unsigned long *addr);
+
+efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg,
+                           unsigned long size, unsigned long align,
+                           unsigned long *addr, unsigned long max);
+
+efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg,
+                                unsigned long *image_addr,
+                                unsigned long image_size,
+                                unsigned long alloc_size,
+                                unsigned long preferred_addr,
+                                unsigned long alignment);
+
+efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
+                                 efi_loaded_image_t *image,
+                                 char *cmd_line, char *option_string,
+                                 unsigned long max_addr,
+                                 unsigned long *load_addr,
+                                 unsigned long *load_size);
+
 #endif /* _LINUX_EFI_H */
index f1331e3..5cc49ea 100644 (file)
@@ -108,11 +108,113 @@ struct xenpf_platform_quirk {
 };
 DEFINE_GUEST_HANDLE_STRUCT(xenpf_platform_quirk_t);
 
+#define XENPF_efi_runtime_call    49
+#define XEN_EFI_get_time                      1
+#define XEN_EFI_set_time                      2
+#define XEN_EFI_get_wakeup_time               3
+#define XEN_EFI_set_wakeup_time               4
+#define XEN_EFI_get_next_high_monotonic_count 5
+#define XEN_EFI_get_variable                  6
+#define XEN_EFI_set_variable                  7
+#define XEN_EFI_get_next_variable_name        8
+#define XEN_EFI_query_variable_info           9
+#define XEN_EFI_query_capsule_capabilities   10
+#define XEN_EFI_update_capsule               11
+
+struct xenpf_efi_runtime_call {
+       uint32_t function;
+       /*
+        * This field is generally used for per sub-function flags (defined
+        * below), except for the XEN_EFI_get_next_high_monotonic_count case,
+        * where it holds the single returned value.
+        */
+       uint32_t misc;
+       xen_ulong_t status;
+       union {
+#define XEN_EFI_GET_TIME_SET_CLEARS_NS 0x00000001
+               struct {
+                       struct xenpf_efi_time {
+                               uint16_t year;
+                               uint8_t month;
+                               uint8_t day;
+                               uint8_t hour;
+                               uint8_t min;
+                               uint8_t sec;
+                               uint32_t ns;
+                               int16_t tz;
+                               uint8_t daylight;
+                       } time;
+                       uint32_t resolution;
+                       uint32_t accuracy;
+               } get_time;
+
+               struct xenpf_efi_time set_time;
+
+#define XEN_EFI_GET_WAKEUP_TIME_ENABLED 0x00000001
+#define XEN_EFI_GET_WAKEUP_TIME_PENDING 0x00000002
+               struct xenpf_efi_time get_wakeup_time;
+
+#define XEN_EFI_SET_WAKEUP_TIME_ENABLE      0x00000001
+#define XEN_EFI_SET_WAKEUP_TIME_ENABLE_ONLY 0x00000002
+               struct xenpf_efi_time set_wakeup_time;
+
+#define XEN_EFI_VARIABLE_NON_VOLATILE       0x00000001
+#define XEN_EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002
+#define XEN_EFI_VARIABLE_RUNTIME_ACCESS     0x00000004
+               struct {
+                       GUEST_HANDLE(void) name;  /* UCS-2/UTF-16 string */
+                       xen_ulong_t size;
+                       GUEST_HANDLE(void) data;
+                       struct xenpf_efi_guid {
+                               uint32_t data1;
+                               uint16_t data2;
+                               uint16_t data3;
+                               uint8_t data4[8];
+                       } vendor_guid;
+               } get_variable, set_variable;
+
+               struct {
+                       xen_ulong_t size;
+                       GUEST_HANDLE(void) name;  /* UCS-2/UTF-16 string */
+                       struct xenpf_efi_guid vendor_guid;
+               } get_next_variable_name;
+
+               struct {
+                       uint32_t attr;
+                       uint64_t max_store_size;
+                       uint64_t remain_store_size;
+                       uint64_t max_size;
+               } query_variable_info;
+
+               struct {
+                       GUEST_HANDLE(void) capsule_header_array;
+                       xen_ulong_t capsule_count;
+                       uint64_t max_capsule_size;
+                       uint32_t reset_type;
+               } query_capsule_capabilities;
+
+               struct {
+                       GUEST_HANDLE(void) capsule_header_array;
+                       xen_ulong_t capsule_count;
+                       uint64_t sg_list; /* machine address */
+               } update_capsule;
+       } u;
+};
+DEFINE_GUEST_HANDLE_STRUCT(xenpf_efi_runtime_call);
+
+#define  XEN_FW_EFI_VERSION        0
+#define  XEN_FW_EFI_CONFIG_TABLE   1
+#define  XEN_FW_EFI_VENDOR         2
+#define  XEN_FW_EFI_MEM_INFO       3
+#define  XEN_FW_EFI_RT_VERSION     4
+
 #define XENPF_firmware_info       50
 #define XEN_FW_DISK_INFO          1 /* from int 13 AH=08/41/48 */
 #define XEN_FW_DISK_MBR_SIGNATURE 2 /* from MBR offset 0x1b8 */
 #define XEN_FW_VBEDDC_INFO        3 /* from int 10 AX=4f15 */
+#define XEN_FW_EFI_INFO           4 /* from EFI */
 #define XEN_FW_KBD_SHIFT_FLAGS    5 /* Int16, Fn02: Get keyboard shift flags. */
+
 struct xenpf_firmware_info {
        /* IN variables. */
        uint32_t type;
@@ -144,6 +246,26 @@ struct xenpf_firmware_info {
                        GUEST_HANDLE(uchar) edid;
                } vbeddc_info; /* XEN_FW_VBEDDC_INFO */
 
+               union xenpf_efi_info {
+                       uint32_t version;
+                       struct {
+                               uint64_t addr;   /* EFI_CONFIGURATION_TABLE */
+                               uint32_t nent;
+                       } cfg;
+                       struct {
+                               uint32_t revision;
+                               uint32_t bufsz;  /* input, in bytes */
+                               GUEST_HANDLE(void) name;
+                               /* UCS-2/UTF-16 string */
+                       } vendor;
+                       struct {
+                               uint64_t addr;
+                               uint64_t size;
+                               uint64_t attr;
+                               uint32_t type;
+                       } mem;
+               } efi_info; /* XEN_FW_EFI_INFO */
+
                uint8_t kbd_shift_flags; /* XEN_FW_KBD_SHIFT_FLAGS */
        } u;
 };
@@ -362,6 +484,7 @@ struct xen_platform_op {
                struct xenpf_read_memtype      read_memtype;
                struct xenpf_microcode_update  microcode;
                struct xenpf_platform_quirk    platform_quirk;
+               struct xenpf_efi_runtime_call  efi_runtime_call;
                struct xenpf_firmware_info     firmware_info;
                struct xenpf_enter_acpi_sleep  enter_acpi_sleep;
                struct xenpf_change_freq       change_freq;
index 0b3149e..7491ee5 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <linux/percpu.h>
 #include <linux/notifier.h>
+#include <linux/efi.h>
 #include <asm/xen/interface.h>
 
 DECLARE_PER_CPU(struct vcpu_info *, xen_vcpu);
@@ -35,4 +36,14 @@ int xen_unmap_domain_mfn_range(struct vm_area_struct *vma,
                               int numpgs, struct page **pages);
 
 bool xen_running_on_version_or_later(unsigned int major, unsigned int minor);
+
+#ifdef CONFIG_XEN_EFI
+extern efi_system_table_t *xen_efi_probe(void);
+#else
+static inline efi_system_table_t __init *xen_efi_probe(void)
+{
+       return NULL;
+}
+#endif
+
 #endif /* INCLUDE_XEN_OPS_H */