Merge branch 'efi-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 16 May 2016 20:06:27 +0000 (13:06 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 16 May 2016 20:06:27 +0000 (13:06 -0700)
Pull EFI updates from Ingo Molnar:
 "The main changes in this cycle were:

   - Drop the unused EFI_SYSTEM_TABLES efi.flags bit and ensure the
     ARM/arm64 EFI System Table mapping is read-only (Ard Biesheuvel)

   - Add a comment to explain that one of the code paths in the x86/pat
     code is only executed for EFI boot (Matt Fleming)

   - Improve Secure Boot status checks on arm64 and handle unexpected
     errors (Linn Crosetto)

   - Remove the global EFI memory map variable 'memmap' as the same
     information is already available in efi::memmap (Matt Fleming)

   - Add EFI Memory Attribute table support for ARM/arm64 (Ard
     Biesheuvel)

   - Add EFI GOP framebuffer support for ARM/arm64 (Ard Biesheuvel)

   - Add EFI Bootloader Control driver for storing reboot(2) data in EFI
     variables for consumption by bootloaders (Jeremy Compostella)

   - Add Core EFI capsule support (Matt Fleming)

   - Add EFI capsule char driver (Kweh, Hock Leong)

   - Unify EFI memory map code for ARM and arm64 (Ard Biesheuvel)

   - Add generic EFI support for detecting when firmware corrupts CPU
     status register bits (like IRQ flags) when performing EFI runtime
     service calls (Mark Rutland)

  ... and other misc cleanups"

* 'efi-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (46 commits)
  efivarfs: Make efivarfs_file_ioctl() static
  efi: Merge boolean flag arguments
  efi/capsule: Move 'capsule' to the stack in efi_capsule_supported()
  efibc: Fix excessive stack footprint warning
  efi/capsule: Make efi_capsule_pending() lockless
  efi: Remove unnecessary (and buggy) .memmap initialization from the Xen EFI driver
  efi/runtime-wrappers: Remove ARCH_EFI_IRQ_FLAGS_MASK #ifdef
  x86/efi: Enable runtime call flag checking
  arm/efi: Enable runtime call flag checking
  arm64/efi: Enable runtime call flag checking
  efi/runtime-wrappers: Detect firmware IRQ flag corruption
  efi/runtime-wrappers: Remove redundant #ifdefs
  x86/efi: Move to generic {__,}efi_call_virt()
  arm/efi: Move to generic {__,}efi_call_virt()
  arm64/efi: Move to generic {__,}efi_call_virt()
  efi/runtime-wrappers: Add {__,}efi_call_virt() templates
  efi/arm-init: Reserve rather than unmap the memory map for ARM as well
  efi: Add misc char driver interface to update EFI firmware
  x86/efi: Force EFI reboot to process pending capsules
  efi: Add 'capsule' update support
  ...

41 files changed:
arch/arm/include/asm/efi.h
arch/arm/kernel/efi.c
arch/arm/kernel/setup.c
arch/arm64/include/asm/efi.h
arch/arm64/kernel/efi.c
arch/arm64/kernel/image.h
arch/ia64/kernel/efi.c
arch/x86/boot/compressed/eboot.c
arch/x86/boot/compressed/eboot.h
arch/x86/include/asm/efi.h
arch/x86/kernel/reboot.c
arch/x86/kernel/sysfb_efi.c
arch/x86/mm/pageattr.c
arch/x86/platform/efi/efi.c
arch/x86/platform/efi/efi_64.c
arch/x86/platform/efi/quirks.c
drivers/firmware/efi/Kconfig
drivers/firmware/efi/Makefile
drivers/firmware/efi/arm-init.c
drivers/firmware/efi/arm-runtime.c
drivers/firmware/efi/capsule-loader.c [new file with mode: 0644]
drivers/firmware/efi/capsule.c [new file with mode: 0644]
drivers/firmware/efi/efi.c
drivers/firmware/efi/efibc.c [new file with mode: 0644]
drivers/firmware/efi/efivars.c
drivers/firmware/efi/fake_mem.c
drivers/firmware/efi/libstub/Makefile
drivers/firmware/efi/libstub/arm-stub.c
drivers/firmware/efi/libstub/arm32-stub.c
drivers/firmware/efi/libstub/efi-stub-helper.c
drivers/firmware/efi/libstub/gop.c [new file with mode: 0644]
drivers/firmware/efi/memattr.c [new file with mode: 0644]
drivers/firmware/efi/reboot.c
drivers/firmware/efi/runtime-wrappers.c
drivers/firmware/efi/vars.c
drivers/video/fbdev/Kconfig
drivers/video/fbdev/efifb.c
drivers/xen/efi.c
fs/efivarfs/file.c
fs/efivarfs/super.c
include/linux/efi.h

index e0eea72deb87eb1ba3967b464a8848256565ee6c..a708fa1f090579228363f8a5f0427db16d31b3a1 100644 (file)
 #include <asm/mach/map.h>
 #include <asm/mmu_context.h>
 #include <asm/pgtable.h>
+#include <asm/ptrace.h>
 
 #ifdef CONFIG_EFI
 void efi_init(void);
 
 int efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md);
+int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md);
 
-#define efi_call_virt(f, ...)                                          \
-({                                                                     \
-       efi_##f##_t *__f;                                               \
-       efi_status_t __s;                                               \
-                                                                       \
-       efi_virtmap_load();                                             \
-       __f = efi.systab->runtime->f;                                   \
-       __s = __f(__VA_ARGS__);                                         \
-       efi_virtmap_unload();                                           \
-       __s;                                                            \
-})
+#define arch_efi_call_virt_setup()     efi_virtmap_load()
+#define arch_efi_call_virt_teardown()  efi_virtmap_unload()
 
-#define __efi_call_virt(f, ...)                                                \
+#define arch_efi_call_virt(f, args...)                                 \
 ({                                                                     \
        efi_##f##_t *__f;                                               \
-                                                                       \
-       efi_virtmap_load();                                             \
        __f = efi.systab->runtime->f;                                   \
-       __f(__VA_ARGS__);                                               \
-       efi_virtmap_unload();                                           \
+       __f(args);                                                      \
 })
 
+#define ARCH_EFI_IRQ_FLAGS_MASK \
+       (PSR_J_BIT | PSR_E_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT | \
+        PSR_T_BIT | MODE_MASK)
+
 static inline void efi_set_pgd(struct mm_struct *mm)
 {
        check_and_switch_context(mm, NULL);
@@ -59,7 +53,16 @@ void efi_virtmap_unload(void);
 
 /* arch specific definitions used by the stub code */
 
-#define efi_call_early(f, ...) sys_table_arg->boottime->f(__VA_ARGS__)
+#define efi_call_early(f, ...)         sys_table_arg->boottime->f(__VA_ARGS__)
+#define __efi_call_early(f, ...)       f(__VA_ARGS__)
+#define efi_is_64bit()                 (false)
+
+struct screen_info *alloc_screen_info(efi_system_table_t *sys_table_arg);
+void free_screen_info(efi_system_table_t *sys_table, struct screen_info *si);
+
+static inline void efifb_setup_from_dmi(struct screen_info *si, const char *opt)
+{
+}
 
 /*
  * A reasonable upper bound for the uncompressed kernel size is 32 MBytes,
index ff8a9d8acfaca727517c76bbefe4bbdd4755f73a..9f43ba012d1077ef553617bff01cf002fba70613 100644 (file)
 #include <asm/mach/map.h>
 #include <asm/mmu_context.h>
 
+static int __init set_permissions(pte_t *ptep, pgtable_t token,
+                                 unsigned long addr, void *data)
+{
+       efi_memory_desc_t *md = data;
+       pte_t pte = *ptep;
+
+       if (md->attribute & EFI_MEMORY_RO)
+               pte = set_pte_bit(pte, __pgprot(L_PTE_RDONLY));
+       if (md->attribute & EFI_MEMORY_XP)
+               pte = set_pte_bit(pte, __pgprot(L_PTE_XN));
+       set_pte_ext(ptep, pte, PTE_EXT_NG);
+       return 0;
+}
+
+int __init efi_set_mapping_permissions(struct mm_struct *mm,
+                                      efi_memory_desc_t *md)
+{
+       unsigned long base, size;
+
+       base = md->virt_addr;
+       size = md->num_pages << EFI_PAGE_SHIFT;
+
+       /*
+        * We can only use apply_to_page_range() if we can guarantee that the
+        * entire region was mapped using pages. This should be the case if the
+        * region does not cover any naturally aligned SECTION_SIZE sized
+        * blocks.
+        */
+       if (round_down(base + size, SECTION_SIZE) <
+           round_up(base, SECTION_SIZE) + SECTION_SIZE)
+               return apply_to_page_range(mm, base, size, set_permissions, md);
+
+       return 0;
+}
+
 int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md)
 {
        struct map_desc desc = {
@@ -34,5 +69,11 @@ int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md)
                desc.type = MT_DEVICE;
 
        create_mapping_late(mm, &desc, true);
+
+       /*
+        * If stricter permissions were specified, apply them now.
+        */
+       if (md->attribute & (EFI_MEMORY_RO | EFI_MEMORY_XP))
+               return efi_set_mapping_permissions(mm, md);
        return 0;
 }
index 2c4bea39cf224f8368cca2b4dba61a5b807a3dde..7d4e2850910ce4cdbeb3e669c7846d782dfe6c8c 100644 (file)
@@ -883,7 +883,8 @@ static void __init request_standard_resources(const struct machine_desc *mdesc)
                request_resource(&ioport_resource, &lp2);
 }
 
-#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE)
+#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE) || \
+    defined(CONFIG_EFI)
 struct screen_info screen_info = {
  .orig_video_lines     = 30,
  .orig_video_cols      = 80,
index 8e88a696c9cbcbd2c8f717ea1c929b87750f1b03..622db3c6474e2d5c51b3a1689869534cf16019ee 100644 (file)
@@ -4,6 +4,7 @@
 #include <asm/io.h>
 #include <asm/mmu_context.h>
 #include <asm/neon.h>
+#include <asm/ptrace.h>
 #include <asm/tlbflush.h>
 
 #ifdef CONFIG_EFI
@@ -14,32 +15,29 @@ extern void efi_init(void);
 
 int efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md);
 
-#define efi_call_virt(f, ...)                                          \
+#define efi_set_mapping_permissions    efi_create_mapping
+
+#define arch_efi_call_virt_setup()                                     \
 ({                                                                     \
-       efi_##f##_t *__f;                                               \
-       efi_status_t __s;                                               \
-                                                                       \
        kernel_neon_begin();                                            \
        efi_virtmap_load();                                             \
-       __f = efi.systab->runtime->f;                                   \
-       __s = __f(__VA_ARGS__);                                         \
-       efi_virtmap_unload();                                           \
-       kernel_neon_end();                                              \
-       __s;                                                            \
 })
 
-#define __efi_call_virt(f, ...)                                                \
+#define arch_efi_call_virt(f, args...)                                 \
 ({                                                                     \
        efi_##f##_t *__f;                                               \
-                                                                       \
-       kernel_neon_begin();                                            \
-       efi_virtmap_load();                                             \
        __f = efi.systab->runtime->f;                                   \
-       __f(__VA_ARGS__);                                               \
+       __f(args);                                                      \
+})
+
+#define arch_efi_call_virt_teardown()                                  \
+({                                                                     \
        efi_virtmap_unload();                                           \
        kernel_neon_end();                                              \
 })
 
+#define ARCH_EFI_IRQ_FLAGS_MASK (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT)
+
 /* arch specific definitions used by the stub code */
 
 /*
@@ -50,7 +48,16 @@ int efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md);
 #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__)
+#define efi_call_early(f, ...)         sys_table_arg->boottime->f(__VA_ARGS__)
+#define __efi_call_early(f, ...)       f(__VA_ARGS__)
+#define efi_is_64bit()                 (true)
+
+#define alloc_screen_info(x...)                &screen_info
+#define free_screen_info(x...)
+
+static inline void efifb_setup_from_dmi(struct screen_info *si, const char *opt)
+{
+}
 
 #define EFI_ALLOC_ALIGN                SZ_64K
 
index b6abc852f2a142123150662bc6dd6bf5c3de62af..78f52488f9ff82461f2933689b44e7b0fcf74b80 100644 (file)
 
 #include <asm/efi.h>
 
-int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md)
+/*
+ * Only regions of type EFI_RUNTIME_SERVICES_CODE need to be
+ * executable, everything else can be mapped with the XN bits
+ * set. Also take the new (optional) RO/XP bits into account.
+ */
+static __init pteval_t create_mapping_protection(efi_memory_desc_t *md)
 {
-       pteval_t prot_val;
+       u64 attr = md->attribute;
+       u32 type = md->type;
 
-       /*
-        * Only regions of type EFI_RUNTIME_SERVICES_CODE need to be
-        * executable, everything else can be mapped with the XN bits
-        * set.
-        */
-       if ((md->attribute & EFI_MEMORY_WB) == 0)
-               prot_val = PROT_DEVICE_nGnRE;
-       else if (md->type == EFI_RUNTIME_SERVICES_CODE ||
-                !PAGE_ALIGNED(md->phys_addr))
-               prot_val = pgprot_val(PAGE_KERNEL_EXEC);
-       else
-               prot_val = pgprot_val(PAGE_KERNEL);
+       if (type == EFI_MEMORY_MAPPED_IO)
+               return PROT_DEVICE_nGnRE;
+
+       if (WARN_ONCE(!PAGE_ALIGNED(md->phys_addr),
+                     "UEFI Runtime regions are not aligned to 64 KB -- buggy firmware?"))
+               /*
+                * If the region is not aligned to the page size of the OS, we
+                * can not use strict permissions, since that would also affect
+                * the mapping attributes of the adjacent regions.
+                */
+               return pgprot_val(PAGE_KERNEL_EXEC);
+
+       /* R-- */
+       if ((attr & (EFI_MEMORY_XP | EFI_MEMORY_RO)) ==
+           (EFI_MEMORY_XP | EFI_MEMORY_RO))
+               return pgprot_val(PAGE_KERNEL_RO);
+
+       /* R-X */
+       if (attr & EFI_MEMORY_RO)
+               return pgprot_val(PAGE_KERNEL_ROX);
+
+       /* RW- */
+       if (attr & EFI_MEMORY_XP || type != EFI_RUNTIME_SERVICES_CODE)
+               return pgprot_val(PAGE_KERNEL);
+
+       /* RWX */
+       return pgprot_val(PAGE_KERNEL_EXEC);
+}
+
+/* we will fill this structure from the stub, so don't put it in .bss */
+struct screen_info screen_info __section(.data);
+
+int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md)
+{
+       pteval_t prot_val = create_mapping_protection(md);
 
        create_pgd_mapping(mm, md->phys_addr, md->virt_addr,
                           md->num_pages << EFI_PAGE_SHIFT,
index 5e360ce88f10ba4110697552a47ea8c42053a02f..1428849aece8f98a52b4661c68c14ea555fe0000 100644 (file)
@@ -112,6 +112,7 @@ __efistub___memset          = KALLSYMS_HIDE(__pi_memset);
 __efistub__text                        = KALLSYMS_HIDE(_text);
 __efistub__end                 = KALLSYMS_HIDE(_end);
 __efistub__edata               = KALLSYMS_HIDE(_edata);
+__efistub_screen_info          = KALLSYMS_HIDE(screen_info);
 
 #endif
 
index 300dac3702f11a13460d1954843bfcbd1ddc2034..bf0865cd438a4a25b2c516274ffed969a1dc64b3 100644 (file)
@@ -531,8 +531,6 @@ efi_init (void)
               efi.systab->hdr.revision >> 16,
               efi.systab->hdr.revision & 0xffff, vendor);
 
-       set_bit(EFI_SYSTEM_TABLES, &efi.flags);
-
        palo_phys      = EFI_INVALID_TABLE_ADDR;
 
        if (efi_config_init(arch_tables) != 0)
index 583d539a41977a2c7397b9fd38e0839d3c41dbb9..52fef606bc54258b7095aa6b4b16eda8fbf16244 100644 (file)
@@ -571,312 +571,6 @@ free_handle:
        efi_call_early(free_pool, pci_handle);
 }
 
-static void
-setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
-                struct efi_pixel_bitmask pixel_info, int pixel_format)
-{
-       if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
-               si->lfb_depth = 32;
-               si->lfb_linelength = pixels_per_scan_line * 4;
-               si->red_size = 8;
-               si->red_pos = 0;
-               si->green_size = 8;
-               si->green_pos = 8;
-               si->blue_size = 8;
-               si->blue_pos = 16;
-               si->rsvd_size = 8;
-               si->rsvd_pos = 24;
-       } else if (pixel_format == PIXEL_BGR_RESERVED_8BIT_PER_COLOR) {
-               si->lfb_depth = 32;
-               si->lfb_linelength = pixels_per_scan_line * 4;
-               si->red_size = 8;
-               si->red_pos = 16;
-               si->green_size = 8;
-               si->green_pos = 8;
-               si->blue_size = 8;
-               si->blue_pos = 0;
-               si->rsvd_size = 8;
-               si->rsvd_pos = 24;
-       } else if (pixel_format == PIXEL_BIT_MASK) {
-               find_bits(pixel_info.red_mask, &si->red_pos, &si->red_size);
-               find_bits(pixel_info.green_mask, &si->green_pos,
-                         &si->green_size);
-               find_bits(pixel_info.blue_mask, &si->blue_pos, &si->blue_size);
-               find_bits(pixel_info.reserved_mask, &si->rsvd_pos,
-                         &si->rsvd_size);
-               si->lfb_depth = si->red_size + si->green_size +
-                       si->blue_size + si->rsvd_size;
-               si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
-       } else {
-               si->lfb_depth = 4;
-               si->lfb_linelength = si->lfb_width / 2;
-               si->red_size = 0;
-               si->red_pos = 0;
-               si->green_size = 0;
-               si->green_pos = 0;
-               si->blue_size = 0;
-               si->blue_pos = 0;
-               si->rsvd_size = 0;
-               si->rsvd_pos = 0;
-       }
-}
-
-static efi_status_t
-__gop_query32(struct efi_graphics_output_protocol_32 *gop32,
-             struct efi_graphics_output_mode_info **info,
-             unsigned long *size, u64 *fb_base)
-{
-       struct efi_graphics_output_protocol_mode_32 *mode;
-       efi_status_t status;
-       unsigned long m;
-
-       m = gop32->mode;
-       mode = (struct efi_graphics_output_protocol_mode_32 *)m;
-
-       status = efi_early->call(gop32->query_mode, gop32,
-                                mode->mode, size, info);
-       if (status != EFI_SUCCESS)
-               return status;
-
-       *fb_base = mode->frame_buffer_base;
-       return status;
-}
-
-static efi_status_t
-setup_gop32(struct screen_info *si, efi_guid_t *proto,
-           unsigned long size, void **gop_handle)
-{
-       struct efi_graphics_output_protocol_32 *gop32, *first_gop;
-       unsigned long nr_gops;
-       u16 width, height;
-       u32 pixels_per_scan_line;
-       u32 ext_lfb_base;
-       u64 fb_base;
-       struct efi_pixel_bitmask pixel_info;
-       int pixel_format;
-       efi_status_t status;
-       u32 *handles = (u32 *)(unsigned long)gop_handle;
-       int i;
-
-       first_gop = NULL;
-       gop32 = NULL;
-
-       nr_gops = size / sizeof(u32);
-       for (i = 0; i < nr_gops; i++) {
-               struct efi_graphics_output_mode_info *info = NULL;
-               efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
-               bool conout_found = false;
-               void *dummy = NULL;
-               u32 h = handles[i];
-               u64 current_fb_base;
-
-               status = efi_call_early(handle_protocol, h,
-                                       proto, (void **)&gop32);
-               if (status != EFI_SUCCESS)
-                       continue;
-
-               status = efi_call_early(handle_protocol, h,
-                                       &conout_proto, &dummy);
-               if (status == EFI_SUCCESS)
-                       conout_found = true;
-
-               status = __gop_query32(gop32, &info, &size, &current_fb_base);
-               if (status == EFI_SUCCESS && (!first_gop || conout_found)) {
-                       /*
-                        * Systems that use the UEFI Console Splitter may
-                        * provide multiple GOP devices, not all of which are
-                        * backed by real hardware. The workaround is to search
-                        * for a GOP implementing the ConOut protocol, and if
-                        * one isn't found, to just fall back to the first GOP.
-                        */
-                       width = info->horizontal_resolution;
-                       height = info->vertical_resolution;
-                       pixel_format = info->pixel_format;
-                       pixel_info = info->pixel_information;
-                       pixels_per_scan_line = info->pixels_per_scan_line;
-                       fb_base = current_fb_base;
-
-                       /*
-                        * Once we've found a GOP supporting ConOut,
-                        * don't bother looking any further.
-                        */
-                       first_gop = gop32;
-                       if (conout_found)
-                               break;
-               }
-       }
-
-       /* Did we find any GOPs? */
-       if (!first_gop)
-               goto out;
-
-       /* EFI framebuffer */
-       si->orig_video_isVGA = VIDEO_TYPE_EFI;
-
-       si->lfb_width = width;
-       si->lfb_height = height;
-       si->lfb_base = fb_base;
-
-       ext_lfb_base = (u64)(unsigned long)fb_base >> 32;
-       if (ext_lfb_base) {
-               si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
-               si->ext_lfb_base = ext_lfb_base;
-       }
-
-       si->pages = 1;
-
-       setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format);
-
-       si->lfb_size = si->lfb_linelength * si->lfb_height;
-
-       si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
-out:
-       return status;
-}
-
-static efi_status_t
-__gop_query64(struct efi_graphics_output_protocol_64 *gop64,
-             struct efi_graphics_output_mode_info **info,
-             unsigned long *size, u64 *fb_base)
-{
-       struct efi_graphics_output_protocol_mode_64 *mode;
-       efi_status_t status;
-       unsigned long m;
-
-       m = gop64->mode;
-       mode = (struct efi_graphics_output_protocol_mode_64 *)m;
-
-       status = efi_early->call(gop64->query_mode, gop64,
-                                mode->mode, size, info);
-       if (status != EFI_SUCCESS)
-               return status;
-
-       *fb_base = mode->frame_buffer_base;
-       return status;
-}
-
-static efi_status_t
-setup_gop64(struct screen_info *si, efi_guid_t *proto,
-           unsigned long size, void **gop_handle)
-{
-       struct efi_graphics_output_protocol_64 *gop64, *first_gop;
-       unsigned long nr_gops;
-       u16 width, height;
-       u32 pixels_per_scan_line;
-       u32 ext_lfb_base;
-       u64 fb_base;
-       struct efi_pixel_bitmask pixel_info;
-       int pixel_format;
-       efi_status_t status;
-       u64 *handles = (u64 *)(unsigned long)gop_handle;
-       int i;
-
-       first_gop = NULL;
-       gop64 = NULL;
-
-       nr_gops = size / sizeof(u64);
-       for (i = 0; i < nr_gops; i++) {
-               struct efi_graphics_output_mode_info *info = NULL;
-               efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
-               bool conout_found = false;
-               void *dummy = NULL;
-               u64 h = handles[i];
-               u64 current_fb_base;
-
-               status = efi_call_early(handle_protocol, h,
-                                       proto, (void **)&gop64);
-               if (status != EFI_SUCCESS)
-                       continue;
-
-               status = efi_call_early(handle_protocol, h,
-                                       &conout_proto, &dummy);
-               if (status == EFI_SUCCESS)
-                       conout_found = true;
-
-               status = __gop_query64(gop64, &info, &size, &current_fb_base);
-               if (status == EFI_SUCCESS && (!first_gop || conout_found)) {
-                       /*
-                        * Systems that use the UEFI Console Splitter may
-                        * provide multiple GOP devices, not all of which are
-                        * backed by real hardware. The workaround is to search
-                        * for a GOP implementing the ConOut protocol, and if
-                        * one isn't found, to just fall back to the first GOP.
-                        */
-                       width = info->horizontal_resolution;
-                       height = info->vertical_resolution;
-                       pixel_format = info->pixel_format;
-                       pixel_info = info->pixel_information;
-                       pixels_per_scan_line = info->pixels_per_scan_line;
-                       fb_base = current_fb_base;
-
-                       /*
-                        * Once we've found a GOP supporting ConOut,
-                        * don't bother looking any further.
-                        */
-                       first_gop = gop64;
-                       if (conout_found)
-                               break;
-               }
-       }
-
-       /* Did we find any GOPs? */
-       if (!first_gop)
-               goto out;
-
-       /* EFI framebuffer */
-       si->orig_video_isVGA = VIDEO_TYPE_EFI;
-
-       si->lfb_width = width;
-       si->lfb_height = height;
-       si->lfb_base = fb_base;
-
-       ext_lfb_base = (u64)(unsigned long)fb_base >> 32;
-       if (ext_lfb_base) {
-               si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
-               si->ext_lfb_base = ext_lfb_base;
-       }
-
-       si->pages = 1;
-
-       setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format);
-
-       si->lfb_size = si->lfb_linelength * si->lfb_height;
-
-       si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
-out:
-       return status;
-}
-
-/*
- * See if we have Graphics Output Protocol
- */
-static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
-                             unsigned long size)
-{
-       efi_status_t status;
-       void **gop_handle = NULL;
-
-       status = efi_call_early(allocate_pool, EFI_LOADER_DATA,
-                               size, (void **)&gop_handle);
-       if (status != EFI_SUCCESS)
-               return status;
-
-       status = efi_call_early(locate_handle,
-                               EFI_LOCATE_BY_PROTOCOL,
-                               proto, NULL, &size, gop_handle);
-       if (status != EFI_SUCCESS)
-               goto free_handle;
-
-       if (efi_early->is64)
-               status = setup_gop64(si, proto, size, gop_handle);
-       else
-               status = setup_gop32(si, proto, size, gop_handle);
-
-free_handle:
-       efi_call_early(free_pool, gop_handle);
-       return status;
-}
-
 static efi_status_t
 setup_uga32(void **uga_handle, unsigned long size, u32 *width, u32 *height)
 {
@@ -1038,7 +732,7 @@ void setup_graphics(struct boot_params *boot_params)
                                EFI_LOCATE_BY_PROTOCOL,
                                &graphics_proto, NULL, &size, gop_handle);
        if (status == EFI_BUFFER_TOO_SMALL)
-               status = setup_gop(si, &graphics_proto, size);
+               status = efi_setup_gop(NULL, si, &graphics_proto, size);
 
        if (status != EFI_SUCCESS) {
                size = 0;
index d487e727f1ec7347ea960b038428998d43a1d6bb..c0223f1a89d71229021ed2d15f99c50ac8c82166 100644 (file)
 
 #define DESC_TYPE_CODE_DATA    (1 << 0)
 
-#define EFI_CONSOLE_OUT_DEVICE_GUID    \
-       EFI_GUID(0xd3b36f2c, 0xd551, 0x11d4, 0x9a, 0x46, 0x0, 0x90, 0x27, \
-                 0x3f, 0xc1, 0x4d)
-
-#define PIXEL_RGB_RESERVED_8BIT_PER_COLOR              0
-#define PIXEL_BGR_RESERVED_8BIT_PER_COLOR              1
-#define PIXEL_BIT_MASK                                 2
-#define PIXEL_BLT_ONLY                                 3
-#define PIXEL_FORMAT_MAX                               4
-
-struct efi_pixel_bitmask {
-       u32 red_mask;
-       u32 green_mask;
-       u32 blue_mask;
-       u32 reserved_mask;
-};
-
-struct efi_graphics_output_mode_info {
-       u32 version;
-       u32 horizontal_resolution;
-       u32 vertical_resolution;
-       int pixel_format;
-       struct efi_pixel_bitmask pixel_information;
-       u32 pixels_per_scan_line;
-} __packed;
-
-struct efi_graphics_output_protocol_mode_32 {
-       u32 max_mode;
-       u32 mode;
-       u32 info;
-       u32 size_of_info;
-       u64 frame_buffer_base;
-       u32 frame_buffer_size;
-} __packed;
-
-struct efi_graphics_output_protocol_mode_64 {
-       u32 max_mode;
-       u32 mode;
-       u64 info;
-       u64 size_of_info;
-       u64 frame_buffer_base;
-       u64 frame_buffer_size;
-} __packed;
-
-struct efi_graphics_output_protocol_mode {
-       u32 max_mode;
-       u32 mode;
-       unsigned long info;
-       unsigned long size_of_info;
-       u64 frame_buffer_base;
-       unsigned long frame_buffer_size;
-} __packed;
-
-struct efi_graphics_output_protocol_32 {
-       u32 query_mode;
-       u32 set_mode;
-       u32 blt;
-       u32 mode;
-};
-
-struct efi_graphics_output_protocol_64 {
-       u64 query_mode;
-       u64 set_mode;
-       u64 blt;
-       u64 mode;
-};
-
-struct efi_graphics_output_protocol {
-       void *query_mode;
-       unsigned long set_mode;
-       unsigned long blt;
-       struct efi_graphics_output_protocol_mode *mode;
-};
-
 struct efi_uga_draw_protocol_32 {
        u32 get_mode;
        u32 set_mode;
index 53748c45e4885f574531f8cd6cf143f6dbc264d6..78d1e7467eae9fb9bd200cb04205c22322b01b4f 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <asm/fpu/api.h>
 #include <asm/pgtable.h>
+#include <asm/processor-flags.h>
 #include <asm/tlb.h>
 
 /*
 
 #define MAX_CMDLINE_ADDRESS    UINT_MAX
 
-#ifdef CONFIG_X86_32
+#define ARCH_EFI_IRQ_FLAGS_MASK        X86_EFLAGS_IF
 
+#ifdef CONFIG_X86_32
 
 extern unsigned long asmlinkage efi_call_phys(void *, ...);
 
+#define arch_efi_call_virt_setup()     kernel_fpu_begin()
+#define arch_efi_call_virt_teardown()  kernel_fpu_end()
+
 /*
  * Wrap all the virtual calls in a way that forces the parameters on the stack.
  */
-
-/* Use this macro if your virtual returns a non-void value */
-#define efi_call_virt(f, args...) \
+#define arch_efi_call_virt(f, args...)                                 \
 ({                                                                     \
-       efi_status_t __s;                                               \
-       kernel_fpu_begin();                                             \
-       __s = ((efi_##f##_t __attribute__((regparm(0)))*)               \
-               efi.systab->runtime->f)(args);                          \
-       kernel_fpu_end();                                               \
-       __s;                                                            \
-})
-
-/* Use this macro if your virtual call does not return any value */
-#define __efi_call_virt(f, args...) \
-({                                                                     \
-       kernel_fpu_begin();                                             \
        ((efi_##f##_t __attribute__((regparm(0)))*)                     \
                efi.systab->runtime->f)(args);                          \
-       kernel_fpu_end();                                               \
 })
 
 #define efi_ioremap(addr, size, type, attr)    ioremap_cache(addr, size)
@@ -78,10 +68,8 @@ struct efi_scratch {
        u64     phys_stack;
 } __packed;
 
-#define efi_call_virt(f, ...)                                          \
+#define arch_efi_call_virt_setup()                                     \
 ({                                                                     \
-       efi_status_t __s;                                               \
-                                                                       \
        efi_sync_low_kernel_mappings();                                 \
        preempt_disable();                                              \
        __kernel_fpu_begin();                                           \
@@ -91,9 +79,13 @@ struct efi_scratch {
                write_cr3((unsigned long)efi_scratch.efi_pgt);          \
                __flush_tlb_all();                                      \
        }                                                               \
-                                                                       \
-       __s = efi_call((void *)efi.systab->runtime->f, __VA_ARGS__);    \
-                                                                       \
+})
+
+#define arch_efi_call_virt(f, args...)                                 \
+       efi_call((void *)efi.systab->runtime->f, args)                  \
+
+#define arch_efi_call_virt_teardown()                                  \
+({                                                                     \
        if (efi_scratch.use_pgd) {                                      \
                write_cr3(efi_scratch.prev_cr3);                        \
                __flush_tlb_all();                                      \
@@ -101,15 +93,8 @@ struct efi_scratch {
                                                                        \
        __kernel_fpu_end();                                             \
        preempt_enable();                                               \
-       __s;                                                            \
 })
 
-/*
- * All X86_64 virt calls return non-void values. Thus, use non-void call for
- * virt calls that would be void on X86_32.
- */
-#define __efi_call_virt(f, args...) efi_call_virt(f, args)
-
 extern void __iomem *__init efi_ioremap(unsigned long addr, unsigned long size,
                                        u32 type, u64 attribute);
 
@@ -180,6 +165,8 @@ static inline bool efi_runtime_supported(void)
 extern struct console early_efi_console;
 extern void parse_efi_setup(u64 phys_addr, u32 data_len);
 
+extern void efifb_setup_from_dmi(struct screen_info *si, const char *opt);
+
 #ifdef CONFIG_EFI_MIXED
 extern void efi_thunk_runtime_setup(void);
 extern efi_status_t efi_thunk_set_virtual_address_map(
@@ -225,6 +212,11 @@ __pure const struct efi_config *__efi_early(void);
 #define efi_call_early(f, ...)                                         \
        __efi_early()->call(__efi_early()->f, __VA_ARGS__);
 
+#define __efi_call_early(f, ...)                                       \
+       __efi_early()->call((unsigned long)f, __VA_ARGS__);
+
+#define efi_is_64bit()         __efi_early()->is64
+
 extern bool efi_reboot_required(void);
 
 #else
index ab0adc0fa5db4da281de34e46c71e2cfbb707c07..a9b31eb815f23e93eae56960879448d7a63fba09 100644 (file)
@@ -535,6 +535,15 @@ static void native_machine_emergency_restart(void)
        mode = reboot_mode == REBOOT_WARM ? 0x1234 : 0;
        *((unsigned short *)__va(0x472)) = mode;
 
+       /*
+        * If an EFI capsule has been registered with the firmware then
+        * override the reboot= parameter.
+        */
+       if (efi_capsule_pending(NULL)) {
+               pr_info("EFI capsule is pending, forcing EFI reboot.\n");
+               reboot_type = BOOT_EFI;
+       }
+
        for (;;) {
                /* Could also try the reset bit in the Hammer NB */
                switch (reboot_type) {
index 5da924bbf0a0f22aa676f5be5a3f97fe4811b087..623965e86b65eda431b8e5fdbc2204c47bcb8b33 100644 (file)
@@ -68,6 +68,21 @@ struct efifb_dmi_info efifb_dmi_list[] = {
        [M_UNKNOWN] = { NULL, 0, 0, 0, 0, OVERRIDE_NONE }
 };
 
+void efifb_setup_from_dmi(struct screen_info *si, const char *opt)
+{
+       int i;
+
+       for (i = 0; i < M_UNKNOWN; i++) {
+               if (efifb_dmi_list[i].base != 0 &&
+                   !strcmp(opt, efifb_dmi_list[i].optname)) {
+                       si->lfb_base = efifb_dmi_list[i].base;
+                       si->lfb_linelength = efifb_dmi_list[i].stride;
+                       si->lfb_width = efifb_dmi_list[i].width;
+                       si->lfb_height = efifb_dmi_list[i].height;
+               }
+       }
+}
+
 #define choose_value(dmivalue, fwvalue, field, flags) ({       \
                typeof(fwvalue) _ret_ = fwvalue;                \
                if ((flags) & (field))                          \
index 01be9ec3bf792f65ac91cc57fe7d64e8e644998f..a1f0e1d0ddc2453a07f86c53ec9791cc9164c6ba 100644 (file)
@@ -1125,8 +1125,14 @@ static int populate_pgd(struct cpa_data *cpa, unsigned long addr)
 static int __cpa_process_fault(struct cpa_data *cpa, unsigned long vaddr,
                               int primary)
 {
-       if (cpa->pgd)
+       if (cpa->pgd) {
+               /*
+                * Right now, we only execute this code path when mapping
+                * the EFI virtual memory map regions, no other users
+                * provide a ->pgd value. This may change in the future.
+                */
                return populate_pgd(cpa, vaddr);
+       }
 
        /*
         * Ignore all non primary paths.
index 994a7df84a7bc713aa4913f04fc7c85992840183..f93545e7dc54e7e2aa19bf494db55eb74080b739 100644 (file)
 #include <asm/rtc.h>
 #include <asm/uv/uv.h>
 
-#define EFI_DEBUG
-
-struct efi_memory_map memmap;
-
 static struct efi efi_phys __initdata;
 static efi_system_table_t efi_systab __initdata;
 
@@ -119,11 +115,10 @@ void efi_get_time(struct timespec *now)
 
 void __init efi_find_mirror(void)
 {
-       void *p;
+       efi_memory_desc_t *md;
        u64 mirror_size = 0, total_size = 0;
 
-       for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
-               efi_memory_desc_t *md = p;
+       for_each_efi_memory_desc(md) {
                unsigned long long start = md->phys_addr;
                unsigned long long size = md->num_pages << EFI_PAGE_SHIFT;
 
@@ -146,10 +141,9 @@ void __init efi_find_mirror(void)
 
 static void __init do_add_efi_memmap(void)
 {
-       void *p;
+       efi_memory_desc_t *md;
 
-       for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
-               efi_memory_desc_t *md = p;
+       for_each_efi_memory_desc(md) {
                unsigned long long start = md->phys_addr;
                unsigned long long size = md->num_pages << EFI_PAGE_SHIFT;
                int e820_type;
@@ -209,47 +203,47 @@ int __init efi_memblock_x86_reserve_range(void)
 #else
        pmap = (e->efi_memmap | ((__u64)e->efi_memmap_hi << 32));
 #endif
-       memmap.phys_map         = pmap;
-       memmap.nr_map           = e->efi_memmap_size /
+       efi.memmap.phys_map     = pmap;
+       efi.memmap.nr_map       = e->efi_memmap_size /
                                  e->efi_memdesc_size;
-       memmap.desc_size        = e->efi_memdesc_size;
-       memmap.desc_version     = e->efi_memdesc_version;
+       efi.memmap.desc_size    = e->efi_memdesc_size;
+       efi.memmap.desc_version = e->efi_memdesc_version;
 
-       memblock_reserve(pmap, memmap.nr_map * memmap.desc_size);
+       WARN(efi.memmap.desc_version != 1,
+            "Unexpected EFI_MEMORY_DESCRIPTOR version %ld",
+            efi.memmap.desc_version);
 
-       efi.memmap = &memmap;
+       memblock_reserve(pmap, efi.memmap.nr_map * efi.memmap.desc_size);
 
        return 0;
 }
 
 void __init efi_print_memmap(void)
 {
-#ifdef EFI_DEBUG
        efi_memory_desc_t *md;
-       void *p;
-       int i;
+       int i = 0;
 
-       for (p = memmap.map, i = 0;
-            p < memmap.map_end;
-            p += memmap.desc_size, i++) {
+       for_each_efi_memory_desc(md) {
                char buf[64];
 
-               md = p;
                pr_info("mem%02u: %s range=[0x%016llx-0x%016llx] (%lluMB)\n",
-                       i, efi_md_typeattr_format(buf, sizeof(buf), md),
+                       i++, efi_md_typeattr_format(buf, sizeof(buf), md),
                        md->phys_addr,
                        md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1,
                        (md->num_pages >> (20 - EFI_PAGE_SHIFT)));
        }
-#endif  /*  EFI_DEBUG  */
 }
 
 void __init efi_unmap_memmap(void)
 {
+       unsigned long size;
+
        clear_bit(EFI_MEMMAP, &efi.flags);
-       if (memmap.map) {
-               early_memunmap(memmap.map, memmap.nr_map * memmap.desc_size);
-               memmap.map = NULL;
+
+       size = efi.memmap.nr_map * efi.memmap.desc_size;
+       if (efi.memmap.map) {
+               early_memunmap(efi.memmap.map, size);
+               efi.memmap.map = NULL;
        }
 }
 
@@ -352,8 +346,6 @@ static int __init efi_systab_init(void *phys)
                       efi.systab->hdr.revision >> 16,
                       efi.systab->hdr.revision & 0xffff);
 
-       set_bit(EFI_SYSTEM_TABLES, &efi.flags);
-
        return 0;
 }
 
@@ -440,17 +432,22 @@ static int __init efi_runtime_init(void)
 
 static int __init efi_memmap_init(void)
 {
+       unsigned long addr, size;
+
        if (efi_enabled(EFI_PARAVIRT))
                return 0;
 
        /* Map the EFI memory map */
-       memmap.map = early_memremap((unsigned long)memmap.phys_map,
-                                  memmap.nr_map * memmap.desc_size);
-       if (memmap.map == NULL) {
+       size = efi.memmap.nr_map * efi.memmap.desc_size;
+       addr = (unsigned long)efi.memmap.phys_map;
+
+       efi.memmap.map = early_memremap(addr, size);
+       if (efi.memmap.map == NULL) {
                pr_err("Could not map the memory map!\n");
                return -ENOMEM;
        }
-       memmap.map_end = memmap.map + (memmap.nr_map * memmap.desc_size);
+
+       efi.memmap.map_end = efi.memmap.map + size;
 
        if (add_efi_memmap)
                do_add_efi_memmap();
@@ -552,12 +549,9 @@ void __init efi_set_executable(efi_memory_desc_t *md, bool executable)
 void __init runtime_code_page_mkexec(void)
 {
        efi_memory_desc_t *md;
-       void *p;
 
        /* Make EFI runtime service code area executable */
-       for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
-               md = p;
-
+       for_each_efi_memory_desc(md) {
                if (md->type != EFI_RUNTIME_SERVICES_CODE)
                        continue;
 
@@ -604,12 +598,10 @@ void __init old_map_region(efi_memory_desc_t *md)
 /* Merge contiguous regions of the same type and attribute */
 static void __init efi_merge_regions(void)
 {
-       void *p;
        efi_memory_desc_t *md, *prev_md = NULL;
 
-       for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
+       for_each_efi_memory_desc(md) {
                u64 prev_size;
-               md = p;
 
                if (!prev_md) {
                        prev_md = md;
@@ -651,30 +643,31 @@ static void __init get_systab_virt_addr(efi_memory_desc_t *md)
 static void __init save_runtime_map(void)
 {
 #ifdef CONFIG_KEXEC_CORE
+       unsigned long desc_size;
        efi_memory_desc_t *md;
-       void *tmp, *p, *q = NULL;
+       void *tmp, *q = NULL;
        int count = 0;
 
        if (efi_enabled(EFI_OLD_MEMMAP))
                return;
 
-       for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
-               md = p;
+       desc_size = efi.memmap.desc_size;
 
+       for_each_efi_memory_desc(md) {
                if (!(md->attribute & EFI_MEMORY_RUNTIME) ||
                    (md->type == EFI_BOOT_SERVICES_CODE) ||
                    (md->type == EFI_BOOT_SERVICES_DATA))
                        continue;
-               tmp = krealloc(q, (count + 1) * memmap.desc_size, GFP_KERNEL);
+               tmp = krealloc(q, (count + 1) * desc_size, GFP_KERNEL);
                if (!tmp)
                        goto out;
                q = tmp;
 
-               memcpy(q + count * memmap.desc_size, md, memmap.desc_size);
+               memcpy(q + count * desc_size, md, desc_size);
                count++;
        }
 
-       efi_runtime_map_setup(q, count, memmap.desc_size);
+       efi_runtime_map_setup(q, count, desc_size);
        return;
 
 out:
@@ -714,10 +707,10 @@ static inline void *efi_map_next_entry_reverse(void *entry)
 {
        /* Initial call */
        if (!entry)
-               return memmap.map_end - memmap.desc_size;
+               return efi.memmap.map_end - efi.memmap.desc_size;
 
-       entry -= memmap.desc_size;
-       if (entry < memmap.map)
+       entry -= efi.memmap.desc_size;
+       if (entry < efi.memmap.map)
                return NULL;
 
        return entry;
@@ -759,10 +752,10 @@ static void *efi_map_next_entry(void *entry)
 
        /* Initial call */
        if (!entry)
-               return memmap.map;
+               return efi.memmap.map;
 
-       entry += memmap.desc_size;
-       if (entry >= memmap.map_end)
+       entry += efi.memmap.desc_size;
+       if (entry >= efi.memmap.map_end)
                return NULL;
 
        return entry;
@@ -776,8 +769,11 @@ static void * __init efi_map_regions(int *count, int *pg_shift)
 {
        void *p, *new_memmap = NULL;
        unsigned long left = 0;
+       unsigned long desc_size;
        efi_memory_desc_t *md;
 
+       desc_size = efi.memmap.desc_size;
+
        p = NULL;
        while ((p = efi_map_next_entry(p))) {
                md = p;
@@ -792,7 +788,7 @@ static void * __init efi_map_regions(int *count, int *pg_shift)
                efi_map_region(md);
                get_systab_virt_addr(md);
 
-               if (left < memmap.desc_size) {
+               if (left < desc_size) {
                        new_memmap = realloc_pages(new_memmap, *pg_shift);
                        if (!new_memmap)
                                return NULL;
@@ -801,10 +797,9 @@ static void * __init efi_map_regions(int *count, int *pg_shift)
                        (*pg_shift)++;
                }
 
-               memcpy(new_memmap + (*count * memmap.desc_size), md,
-                      memmap.desc_size);
+               memcpy(new_memmap + (*count * desc_size), md, desc_size);
 
-               left -= memmap.desc_size;
+               left -= desc_size;
                (*count)++;
        }
 
@@ -816,7 +811,6 @@ static void __init kexec_enter_virtual_mode(void)
 #ifdef CONFIG_KEXEC_CORE
        efi_memory_desc_t *md;
        unsigned int num_pages;
-       void *p;
 
        efi.systab = NULL;
 
@@ -840,8 +834,7 @@ static void __init kexec_enter_virtual_mode(void)
        * Map efi regions which were passed via setup_data. The virt_addr is a
        * fixed addr which was used in first kernel of a kexec boot.
        */
-       for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
-               md = p;
+       for_each_efi_memory_desc(md) {
                efi_map_region_fixed(md); /* FIXME: add error handling */
                get_systab_virt_addr(md);
        }
@@ -850,10 +843,10 @@ static void __init kexec_enter_virtual_mode(void)
 
        BUG_ON(!efi.systab);
 
-       num_pages = ALIGN(memmap.nr_map * memmap.desc_size, PAGE_SIZE);
+       num_pages = ALIGN(efi.memmap.nr_map * efi.memmap.desc_size, PAGE_SIZE);
        num_pages >>= PAGE_SHIFT;
 
-       if (efi_setup_page_tables(memmap.phys_map, num_pages)) {
+       if (efi_setup_page_tables(efi.memmap.phys_map, num_pages)) {
                clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
                return;
        }
@@ -937,16 +930,16 @@ static void __init __efi_enter_virtual_mode(void)
 
        if (efi_is_native()) {
                status = phys_efi_set_virtual_address_map(
-                               memmap.desc_size * count,
-                               memmap.desc_size,
-                               memmap.desc_version,
+                               efi.memmap.desc_size * count,
+                               efi.memmap.desc_size,
+                               efi.memmap.desc_version,
                                (efi_memory_desc_t *)__pa(new_memmap));
        } else {
                status = efi_thunk_set_virtual_address_map(
                                efi_phys.set_virtual_address_map,
-                               memmap.desc_size * count,
-                               memmap.desc_size,
-                               memmap.desc_version,
+                               efi.memmap.desc_size * count,
+                               efi.memmap.desc_size,
+                               efi.memmap.desc_version,
                                (efi_memory_desc_t *)__pa(new_memmap));
        }
 
@@ -1011,13 +1004,11 @@ void __init efi_enter_virtual_mode(void)
 u32 efi_mem_type(unsigned long phys_addr)
 {
        efi_memory_desc_t *md;
-       void *p;
 
        if (!efi_enabled(EFI_MEMMAP))
                return 0;
 
-       for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
-               md = p;
+       for_each_efi_memory_desc(md) {
                if ((md->phys_addr <= phys_addr) &&
                    (phys_addr < (md->phys_addr +
                                  (md->num_pages << EFI_PAGE_SHIFT))))
index 49e4dd4a1f58257630c8b341bf8b12922200cd2e..6e7242be1c8744b89af4a96587fb79d3a387e77a 100644 (file)
@@ -55,14 +55,12 @@ struct efi_scratch efi_scratch;
 static void __init early_code_mapping_set_exec(int executable)
 {
        efi_memory_desc_t *md;
-       void *p;
 
        if (!(__supported_pte_mask & _PAGE_NX))
                return;
 
        /* Make EFI service code area executable */
-       for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
-               md = p;
+       for_each_efi_memory_desc(md) {
                if (md->type == EFI_RUNTIME_SERVICES_CODE ||
                    md->type == EFI_BOOT_SERVICES_CODE)
                        efi_set_executable(md, executable);
@@ -253,7 +251,7 @@ int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
         * Map all of RAM so that we can access arguments in the 1:1
         * mapping when making EFI runtime calls.
         */
-       for_each_efi_memory_desc(&memmap, md) {
+       for_each_efi_memory_desc(md) {
                if (md->type != EFI_CONVENTIONAL_MEMORY &&
                    md->type != EFI_LOADER_DATA &&
                    md->type != EFI_LOADER_CODE)
@@ -398,7 +396,6 @@ void __init efi_runtime_update_mappings(void)
        unsigned long pfn;
        pgd_t *pgd = efi_pgd;
        efi_memory_desc_t *md;
-       void *p;
 
        if (efi_enabled(EFI_OLD_MEMMAP)) {
                if (__supported_pte_mask & _PAGE_NX)
@@ -409,9 +406,8 @@ void __init efi_runtime_update_mappings(void)
        if (!efi_enabled(EFI_NX_PE_DATA))
                return;
 
-       for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
+       for_each_efi_memory_desc(md) {
                unsigned long pf = 0;
-               md = p;
 
                if (!(md->attribute & EFI_MEMORY_RUNTIME))
                        continue;
index ab50ada1d56e4f87f6fff3207995ea32312fb37b..097cb09d917b1f826c13d6fb498115a2dd690d31 100644 (file)
@@ -195,10 +195,9 @@ static bool can_free_region(u64 start, u64 size)
 */
 void __init efi_reserve_boot_services(void)
 {
-       void *p;
+       efi_memory_desc_t *md;
 
-       for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
-               efi_memory_desc_t *md = p;
+       for_each_efi_memory_desc(md) {
                u64 start = md->phys_addr;
                u64 size = md->num_pages << EFI_PAGE_SHIFT;
                bool already_reserved;
@@ -250,10 +249,9 @@ void __init efi_reserve_boot_services(void)
 
 void __init efi_free_boot_services(void)
 {
-       void *p;
+       efi_memory_desc_t *md;
 
-       for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
-               efi_memory_desc_t *md = p;
+       for_each_efi_memory_desc(md) {
                unsigned long long start = md->phys_addr;
                unsigned long long size = md->num_pages << EFI_PAGE_SHIFT;
 
index e1670d533f9742e94b505ba0684d9373956c8bb0..6394152f648f4f0262a4c5be42a7ae0cb7aaa7a8 100644 (file)
@@ -87,6 +87,31 @@ config EFI_RUNTIME_WRAPPERS
 config EFI_ARMSTUB
        bool
 
+config EFI_BOOTLOADER_CONTROL
+       tristate "EFI Bootloader Control"
+       depends on EFI_VARS
+       default n
+       ---help---
+         This module installs a reboot hook, such that if reboot() is
+         invoked with a string argument NNN, "NNN" is copied to the
+         "LoaderEntryOneShot" EFI variable, to be read by the
+         bootloader. If the string matches one of the boot labels
+         defined in its configuration, the bootloader will boot once
+         to that label. The "LoaderEntryRebootReason" EFI variable is
+         set with the reboot reason: "reboot" or "shutdown". The
+         bootloader reads this reboot reason and takes particular
+         action according to its policy.
+
+config EFI_CAPSULE_LOADER
+       tristate "EFI capsule loader"
+       depends on EFI
+       help
+         This option exposes a loader interface "/dev/efi_capsule_loader" for
+         users to load EFI capsules. This driver requires working runtime
+         capsule support in the firmware, which many OEMs do not provide.
+
+         Most users should say N.
+
 endmenu
 
 config UEFI_CPER
index 62e654f255f4d1e4cb1ee1b955e8fd03ca689221..a219640f881f0306eb1888d924c4980277044e68 100644 (file)
@@ -9,7 +9,8 @@
 #
 KASAN_SANITIZE_runtime-wrappers.o      := n
 
-obj-$(CONFIG_EFI)                      += efi.o vars.o reboot.o
+obj-$(CONFIG_EFI)                      += efi.o vars.o reboot.o memattr.o
+obj-$(CONFIG_EFI)                      += capsule.o
 obj-$(CONFIG_EFI_VARS)                 += efivars.o
 obj-$(CONFIG_EFI_ESRT)                 += esrt.o
 obj-$(CONFIG_EFI_VARS_PSTORE)          += efi-pstore.o
@@ -18,7 +19,9 @@ obj-$(CONFIG_EFI_RUNTIME_MAP)         += runtime-map.o
 obj-$(CONFIG_EFI_RUNTIME_WRAPPERS)     += runtime-wrappers.o
 obj-$(CONFIG_EFI_STUB)                 += libstub/
 obj-$(CONFIG_EFI_FAKE_MEMMAP)          += fake_mem.o
+obj-$(CONFIG_EFI_BOOTLOADER_CONTROL)   += efibc.o
 
 arm-obj-$(CONFIG_EFI)                  := arm-init.o arm-runtime.o
 obj-$(CONFIG_ARM)                      += $(arm-obj-y)
 obj-$(CONFIG_ARM64)                    += $(arm-obj-y)
+obj-$(CONFIG_EFI_CAPSULE_LOADER)       += capsule-loader.o
index 8714f8c271babfff5566765605b5429f33bfa5d8..ef90f0c4b70a735def7eb9c56aec980f7813a8fe 100644 (file)
  *
  */
 
+#define pr_fmt(fmt)    "efi: " fmt
+
 #include <linux/efi.h>
 #include <linux/init.h>
 #include <linux/memblock.h>
 #include <linux/mm_types.h>
 #include <linux/of.h>
 #include <linux/of_fdt.h>
+#include <linux/platform_device.h>
+#include <linux/screen_info.h>
 
 #include <asm/efi.h>
 
-struct efi_memory_map memmap;
-
 u64 efi_system_table;
 
 static int __init is_normal_ram(efi_memory_desc_t *md)
@@ -40,7 +42,7 @@ static phys_addr_t efi_to_phys(unsigned long addr)
 {
        efi_memory_desc_t *md;
 
-       for_each_efi_memory_desc(&memmap, md) {
+       for_each_efi_memory_desc(md) {
                if (!(md->attribute & EFI_MEMORY_RUNTIME))
                        continue;
                if (md->virt_addr == 0)
@@ -53,6 +55,36 @@ static phys_addr_t efi_to_phys(unsigned long addr)
        return addr;
 }
 
+static __initdata unsigned long screen_info_table = EFI_INVALID_TABLE_ADDR;
+
+static __initdata efi_config_table_type_t arch_tables[] = {
+       {LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, NULL, &screen_info_table},
+       {NULL_GUID, NULL, NULL}
+};
+
+static void __init init_screen_info(void)
+{
+       struct screen_info *si;
+
+       if (screen_info_table != EFI_INVALID_TABLE_ADDR) {
+               si = early_memremap_ro(screen_info_table, sizeof(*si));
+               if (!si) {
+                       pr_err("Could not map screen_info config table\n");
+                       return;
+               }
+               screen_info = *si;
+               early_memunmap(si, sizeof(*si));
+
+               /* dummycon on ARM needs non-zero values for columns/lines */
+               screen_info.orig_video_cols = 80;
+               screen_info.orig_video_lines = 25;
+       }
+
+       if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI &&
+           memblock_is_map_memory(screen_info.lfb_base))
+               memblock_mark_nomap(screen_info.lfb_base, screen_info.lfb_size);
+}
+
 static int __init uefi_init(void)
 {
        efi_char16_t *c16;
@@ -85,6 +117,8 @@ static int __init uefi_init(void)
                        efi.systab->hdr.revision >> 16,
                        efi.systab->hdr.revision & 0xffff);
 
+       efi.runtime_version = efi.systab->hdr.revision;
+
        /* Show what we know for posterity */
        c16 = early_memremap_ro(efi_to_phys(efi.systab->fw_vendor),
                                sizeof(vendor) * sizeof(efi_char16_t));
@@ -108,7 +142,8 @@ static int __init uefi_init(void)
                goto out;
        }
        retval = efi_config_parse_tables(config_tables, efi.systab->nr_tables,
-                                        sizeof(efi_config_table_t), NULL);
+                                        sizeof(efi_config_table_t),
+                                        arch_tables);
 
        early_memunmap(config_tables, table_size);
 out:
@@ -143,7 +178,7 @@ static __init void reserve_regions(void)
        if (efi_enabled(EFI_DBG))
                pr_info("Processing EFI memory map:\n");
 
-       for_each_efi_memory_desc(&memmap, md) {
+       for_each_efi_memory_desc(md) {
                paddr = md->phys_addr;
                npages = md->num_pages;
 
@@ -184,9 +219,9 @@ void __init efi_init(void)
 
        efi_system_table = params.system_table;
 
-       memmap.phys_map = params.mmap;
-       memmap.map = early_memremap_ro(params.mmap, params.mmap_size);
-       if (memmap.map == NULL) {
+       efi.memmap.phys_map = params.mmap;
+       efi.memmap.map = early_memremap_ro(params.mmap, params.mmap_size);
+       if (efi.memmap.map == NULL) {
                /*
                * If we are booting via UEFI, the UEFI memory map is the only
                * description of memory we have, so there is little point in
@@ -194,28 +229,37 @@ void __init efi_init(void)
                */
                panic("Unable to map EFI memory map.\n");
        }
-       memmap.map_end = memmap.map + params.mmap_size;
-       memmap.desc_size = params.desc_size;
-       memmap.desc_version = params.desc_ver;
+       efi.memmap.map_end = efi.memmap.map + params.mmap_size;
+       efi.memmap.desc_size = params.desc_size;
+       efi.memmap.desc_version = params.desc_ver;
+
+       WARN(efi.memmap.desc_version != 1,
+            "Unexpected EFI_MEMORY_DESCRIPTOR version %ld",
+             efi.memmap.desc_version);
 
        if (uefi_init() < 0)
                return;
 
        reserve_regions();
-       early_memunmap(memmap.map, params.mmap_size);
+       efi_memattr_init();
+       early_memunmap(efi.memmap.map, params.mmap_size);
 
-       if (IS_ENABLED(CONFIG_ARM)) {
-               /*
-                * ARM currently does not allow ioremap_cache() to be called on
-                * memory regions that are covered by struct page. So remove the
-                * UEFI memory map from the linear mapping.
-                */
-               memblock_mark_nomap(params.mmap & PAGE_MASK,
-                                   PAGE_ALIGN(params.mmap_size +
-                                              (params.mmap & ~PAGE_MASK)));
-       } else {
-               memblock_reserve(params.mmap & PAGE_MASK,
-                                PAGE_ALIGN(params.mmap_size +
-                                           (params.mmap & ~PAGE_MASK)));
-       }
+       memblock_reserve(params.mmap & PAGE_MASK,
+                        PAGE_ALIGN(params.mmap_size +
+                                   (params.mmap & ~PAGE_MASK)));
+
+       init_screen_info();
+}
+
+static int __init register_gop_device(void)
+{
+       void *pd;
+
+       if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
+               return 0;
+
+       pd = platform_device_register_data(NULL, "efi-framebuffer", 0,
+                                          &screen_info, sizeof(screen_info));
+       return PTR_ERR_OR_ZERO(pd);
 }
+subsys_initcall(register_gop_device);
index 6ae21e41a429402d132d8d5be215d5adde64d08c..17ccf0a8787a2cf3f7ea1869e2e06801da2c46ef 100644 (file)
@@ -42,11 +42,13 @@ static struct mm_struct efi_mm = {
 static bool __init efi_virtmap_init(void)
 {
        efi_memory_desc_t *md;
+       bool systab_found;
 
        efi_mm.pgd = pgd_alloc(&efi_mm);
        init_new_context(NULL, &efi_mm);
 
-       for_each_efi_memory_desc(&memmap, md) {
+       systab_found = false;
+       for_each_efi_memory_desc(md) {
                phys_addr_t phys = md->phys_addr;
                int ret;
 
@@ -64,7 +66,25 @@ static bool __init efi_virtmap_init(void)
                                &phys, ret);
                        return false;
                }
+               /*
+                * If this entry covers the address of the UEFI system table,
+                * calculate and record its virtual address.
+                */
+               if (efi_system_table >= phys &&
+                   efi_system_table < phys + (md->num_pages * EFI_PAGE_SIZE)) {
+                       efi.systab = (void *)(unsigned long)(efi_system_table -
+                                                            phys + md->virt_addr);
+                       systab_found = true;
+               }
+       }
+       if (!systab_found) {
+               pr_err("No virtual mapping found for the UEFI System Table\n");
+               return false;
        }
+
+       if (efi_memattr_apply_permissions(&efi_mm, efi_set_mapping_permissions))
+               return false;
+
        return true;
 }
 
@@ -89,26 +109,17 @@ static int __init arm_enable_runtime_services(void)
 
        pr_info("Remapping and enabling EFI services.\n");
 
-       mapsize = memmap.map_end - memmap.map;
-       memmap.map = (__force void *)ioremap_cache(memmap.phys_map,
-                                                  mapsize);
-       if (!memmap.map) {
-               pr_err("Failed to remap EFI memory map\n");
-               return -ENOMEM;
-       }
-       memmap.map_end = memmap.map + mapsize;
-       efi.memmap = &memmap;
+       mapsize = efi.memmap.map_end - efi.memmap.map;
 
-       efi.systab = (__force void *)ioremap_cache(efi_system_table,
-                                                  sizeof(efi_system_table_t));
-       if (!efi.systab) {
-               pr_err("Failed to remap EFI System Table\n");
+       efi.memmap.map = memremap(efi.memmap.phys_map, mapsize, MEMREMAP_WB);
+       if (!efi.memmap.map) {
+               pr_err("Failed to remap EFI memory map\n");
                return -ENOMEM;
        }
-       set_bit(EFI_SYSTEM_TABLES, &efi.flags);
+       efi.memmap.map_end = efi.memmap.map + mapsize;
 
        if (!efi_virtmap_init()) {
-               pr_err("No UEFI virtual mapping was installed -- runtime services will not be available\n");
+               pr_err("UEFI virtual mapping missing or invalid -- runtime services will not be available\n");
                return -ENOMEM;
        }
 
@@ -116,8 +127,6 @@ static int __init arm_enable_runtime_services(void)
        efi_native_runtime_setup();
        set_bit(EFI_RUNTIME_SERVICES, &efi.flags);
 
-       efi.runtime_version = efi.systab->hdr.revision;
-
        return 0;
 }
 early_initcall(arm_enable_runtime_services);
diff --git a/drivers/firmware/efi/capsule-loader.c b/drivers/firmware/efi/capsule-loader.c
new file mode 100644 (file)
index 0000000..c99c24b
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ * EFI capsule loader driver.
+ *
+ * Copyright 2015 Intel Corporation
+ *
+ * This file is part of the Linux kernel, and is made available under
+ * the terms of the GNU General Public License version 2.
+ */
+
+#define pr_fmt(fmt) "efi: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/highmem.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/efi.h>
+
+#define NO_FURTHER_WRITE_ACTION -1
+
+struct capsule_info {
+       bool            header_obtained;
+       int             reset_type;
+       long            index;
+       size_t          count;
+       size_t          total_size;
+       struct page     **pages;
+       size_t          page_bytes_remain;
+};
+
+/**
+ * efi_free_all_buff_pages - free all previous allocated buffer pages
+ * @cap_info: pointer to current instance of capsule_info structure
+ *
+ *     In addition to freeing buffer pages, it flags NO_FURTHER_WRITE_ACTION
+ *     to cease processing data in subsequent write(2) calls until close(2)
+ *     is called.
+ **/
+static void efi_free_all_buff_pages(struct capsule_info *cap_info)
+{
+       while (cap_info->index > 0)
+               __free_page(cap_info->pages[--cap_info->index]);
+
+       cap_info->index = NO_FURTHER_WRITE_ACTION;
+}
+
+/**
+ * efi_capsule_setup_info - obtain the efi capsule header in the binary and
+ *                         setup capsule_info structure
+ * @cap_info: pointer to current instance of capsule_info structure
+ * @kbuff: a mapped first page buffer pointer
+ * @hdr_bytes: the total received number of bytes for efi header
+ **/
+static ssize_t efi_capsule_setup_info(struct capsule_info *cap_info,
+                                     void *kbuff, size_t hdr_bytes)
+{
+       efi_capsule_header_t *cap_hdr;
+       size_t pages_needed;
+       int ret;
+       void *temp_page;
+
+       /* Only process data block that is larger than efi header size */
+       if (hdr_bytes < sizeof(efi_capsule_header_t))
+               return 0;
+
+       /* Reset back to the correct offset of header */
+       cap_hdr = kbuff - cap_info->count;
+       pages_needed = ALIGN(cap_hdr->imagesize, PAGE_SIZE) >> PAGE_SHIFT;
+
+       if (pages_needed == 0) {
+               pr_err("%s: pages count invalid\n", __func__);
+               return -EINVAL;
+       }
+
+       /* Check if the capsule binary supported */
+       ret = efi_capsule_supported(cap_hdr->guid, cap_hdr->flags,
+                                   cap_hdr->imagesize,
+                                   &cap_info->reset_type);
+       if (ret) {
+               pr_err("%s: efi_capsule_supported() failed\n",
+                      __func__);
+               return ret;
+       }
+
+       cap_info->total_size = cap_hdr->imagesize;
+       temp_page = krealloc(cap_info->pages,
+                            pages_needed * sizeof(void *),
+                            GFP_KERNEL | __GFP_ZERO);
+       if (!temp_page) {
+               pr_debug("%s: krealloc() failed\n", __func__);
+               return -ENOMEM;
+       }
+
+       cap_info->pages = temp_page;
+       cap_info->header_obtained = true;
+
+       return 0;
+}
+
+/**
+ * efi_capsule_submit_update - invoke the efi_capsule_update API once binary
+ *                            upload done
+ * @cap_info: pointer to current instance of capsule_info structure
+ **/
+static ssize_t efi_capsule_submit_update(struct capsule_info *cap_info)
+{
+       int ret;
+       void *cap_hdr_temp;
+
+       cap_hdr_temp = kmap(cap_info->pages[0]);
+       if (!cap_hdr_temp) {
+               pr_debug("%s: kmap() failed\n", __func__);
+               return -EFAULT;
+       }
+
+       ret = efi_capsule_update(cap_hdr_temp, cap_info->pages);
+       kunmap(cap_info->pages[0]);
+       if (ret) {
+               pr_err("%s: efi_capsule_update() failed\n", __func__);
+               return ret;
+       }
+
+       /* Indicate capsule binary uploading is done */
+       cap_info->index = NO_FURTHER_WRITE_ACTION;
+       pr_info("%s: Successfully upload capsule file with reboot type '%s'\n",
+               __func__, !cap_info->reset_type ? "RESET_COLD" :
+               cap_info->reset_type == 1 ? "RESET_WARM" :
+               "RESET_SHUTDOWN");
+       return 0;
+}
+
+/**
+ * efi_capsule_write - store the capsule binary and pass it to
+ *                    efi_capsule_update() API
+ * @file: file pointer
+ * @buff: buffer pointer
+ * @count: number of bytes in @buff
+ * @offp: not used
+ *
+ *     Expectation:
+ *     - A user space tool should start at the beginning of capsule binary and
+ *       pass data in sequentially.
+ *     - Users should close and re-open this file note in order to upload more
+ *       capsules.
+ *     - After an error returned, user should close the file and restart the
+ *       operation for the next try otherwise -EIO will be returned until the
+ *       file is closed.
+ *     - An EFI capsule header must be located at the beginning of capsule
+ *       binary file and passed in as first block data of write operation.
+ **/
+static ssize_t efi_capsule_write(struct file *file, const char __user *buff,
+                                size_t count, loff_t *offp)
+{
+       int ret = 0;
+       struct capsule_info *cap_info = file->private_data;
+       struct page *page;
+       void *kbuff = NULL;
+       size_t write_byte;
+
+       if (count == 0)
+               return 0;
+
+       /* Return error while NO_FURTHER_WRITE_ACTION is flagged */
+       if (cap_info->index < 0)
+               return -EIO;
+
+       /* Only alloc a new page when previous page is full */
+       if (!cap_info->page_bytes_remain) {
+               page = alloc_page(GFP_KERNEL);
+               if (!page) {
+                       pr_debug("%s: alloc_page() failed\n", __func__);
+                       ret = -ENOMEM;
+                       goto failed;
+               }
+
+               cap_info->pages[cap_info->index++] = page;
+               cap_info->page_bytes_remain = PAGE_SIZE;
+       }
+
+       page = cap_info->pages[cap_info->index - 1];
+
+       kbuff = kmap(page);
+       if (!kbuff) {
+               pr_debug("%s: kmap() failed\n", __func__);
+               ret = -EFAULT;
+               goto failed;
+       }
+       kbuff += PAGE_SIZE - cap_info->page_bytes_remain;
+
+       /* Copy capsule binary data from user space to kernel space buffer */
+       write_byte = min_t(size_t, count, cap_info->page_bytes_remain);
+       if (copy_from_user(kbuff, buff, write_byte)) {
+               pr_debug("%s: copy_from_user() failed\n", __func__);
+               ret = -EFAULT;
+               goto fail_unmap;
+       }
+       cap_info->page_bytes_remain -= write_byte;
+
+       /* Setup capsule binary info structure */
+       if (!cap_info->header_obtained) {
+               ret = efi_capsule_setup_info(cap_info, kbuff,
+                                            cap_info->count + write_byte);
+               if (ret)
+                       goto fail_unmap;
+       }
+
+       cap_info->count += write_byte;
+       kunmap(page);
+
+       /* Submit the full binary to efi_capsule_update() API */
+       if (cap_info->header_obtained &&
+           cap_info->count >= cap_info->total_size) {
+               if (cap_info->count > cap_info->total_size) {
+                       pr_err("%s: upload size exceeded header defined size\n",
+                              __func__);
+                       ret = -EINVAL;
+                       goto failed;
+               }
+
+               ret = efi_capsule_submit_update(cap_info);
+               if (ret)
+                       goto failed;
+       }
+
+       return write_byte;
+
+fail_unmap:
+       kunmap(page);
+failed:
+       efi_free_all_buff_pages(cap_info);
+       return ret;
+}
+
+/**
+ * efi_capsule_flush - called by file close or file flush
+ * @file: file pointer
+ * @id: not used
+ *
+ *     If a capsule is being partially uploaded then calling this function
+ *     will be treated as upload termination and will free those completed
+ *     buffer pages and -ECANCELED will be returned.
+ **/
+static int efi_capsule_flush(struct file *file, fl_owner_t id)
+{
+       int ret = 0;
+       struct capsule_info *cap_info = file->private_data;
+
+       if (cap_info->index > 0) {
+               pr_err("%s: capsule upload not complete\n", __func__);
+               efi_free_all_buff_pages(cap_info);
+               ret = -ECANCELED;
+       }
+
+       return ret;
+}
+
+/**
+ * efi_capsule_release - called by file close
+ * @inode: not used
+ * @file: file pointer
+ *
+ *     We will not free successfully submitted pages since efi update
+ *     requires data to be maintained across system reboot.
+ **/
+static int efi_capsule_release(struct inode *inode, struct file *file)
+{
+       struct capsule_info *cap_info = file->private_data;
+
+       kfree(cap_info->pages);
+       kfree(file->private_data);
+       file->private_data = NULL;
+       return 0;
+}
+
+/**
+ * efi_capsule_open - called by file open
+ * @inode: not used
+ * @file: file pointer
+ *
+ *     Will allocate each capsule_info memory for each file open call.
+ *     This provided the capability to support multiple file open feature
+ *     where user is not needed to wait for others to finish in order to
+ *     upload their capsule binary.
+ **/
+static int efi_capsule_open(struct inode *inode, struct file *file)
+{
+       struct capsule_info *cap_info;
+
+       cap_info = kzalloc(sizeof(*cap_info), GFP_KERNEL);
+       if (!cap_info)
+               return -ENOMEM;
+
+       cap_info->pages = kzalloc(sizeof(void *), GFP_KERNEL);
+       if (!cap_info->pages) {
+               kfree(cap_info);
+               return -ENOMEM;
+       }
+
+       file->private_data = cap_info;
+
+       return 0;
+}
+
+static const struct file_operations efi_capsule_fops = {
+       .owner = THIS_MODULE,
+       .open = efi_capsule_open,
+       .write = efi_capsule_write,
+       .flush = efi_capsule_flush,
+       .release = efi_capsule_release,
+       .llseek = no_llseek,
+};
+
+static struct miscdevice efi_capsule_misc = {
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = "efi_capsule_loader",
+       .fops = &efi_capsule_fops,
+};
+
+static int __init efi_capsule_loader_init(void)
+{
+       int ret;
+
+       if (!efi_enabled(EFI_RUNTIME_SERVICES))
+               return -ENODEV;
+
+       ret = misc_register(&efi_capsule_misc);
+       if (ret)
+               pr_err("%s: Failed to register misc char file note\n",
+                      __func__);
+
+       return ret;
+}
+module_init(efi_capsule_loader_init);
+
+static void __exit efi_capsule_loader_exit(void)
+{
+       misc_deregister(&efi_capsule_misc);
+}
+module_exit(efi_capsule_loader_exit);
+
+MODULE_DESCRIPTION("EFI capsule firmware binary loader");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/firmware/efi/capsule.c b/drivers/firmware/efi/capsule.c
new file mode 100644 (file)
index 0000000..53b9fd2
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ * EFI capsule support.
+ *
+ * Copyright 2013 Intel Corporation; author Matt Fleming
+ *
+ * This file is part of the Linux kernel, and is made available under
+ * the terms of the GNU General Public License version 2.
+ */
+
+#define pr_fmt(fmt) "efi: " fmt
+
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/highmem.h>
+#include <linux/efi.h>
+#include <linux/vmalloc.h>
+#include <asm/io.h>
+
+typedef struct {
+       u64 length;
+       u64 data;
+} efi_capsule_block_desc_t;
+
+static bool capsule_pending;
+static bool stop_capsules;
+static int efi_reset_type = -1;
+
+/*
+ * capsule_mutex serialises access to both capsule_pending and
+ * efi_reset_type and stop_capsules.
+ */
+static DEFINE_MUTEX(capsule_mutex);
+
+/**
+ * efi_capsule_pending - has a capsule been passed to the firmware?
+ * @reset_type: store the type of EFI reset if capsule is pending
+ *
+ * To ensure that the registered capsule is processed correctly by the
+ * firmware we need to perform a specific type of reset. If a capsule is
+ * pending return the reset type in @reset_type.
+ *
+ * This function will race with callers of efi_capsule_update(), for
+ * example, calling this function while somebody else is in
+ * efi_capsule_update() but hasn't reached efi_capsue_update_locked()
+ * will miss the updates to capsule_pending and efi_reset_type after
+ * efi_capsule_update_locked() completes.
+ *
+ * A non-racy use is from platform reboot code because we use
+ * system_state to ensure no capsules can be sent to the firmware once
+ * we're at SYSTEM_RESTART. See efi_capsule_update_locked().
+ */
+bool efi_capsule_pending(int *reset_type)
+{
+       if (!capsule_pending)
+               return false;
+
+       if (reset_type)
+               *reset_type = efi_reset_type;
+
+       return true;
+}
+
+/*
+ * Whitelist of EFI capsule flags that we support.
+ *
+ * We do not handle EFI_CAPSULE_INITIATE_RESET because that would
+ * require us to prepare the kernel for reboot. Refuse to load any
+ * capsules with that flag and any other flags that we do not know how
+ * to handle.
+ */
+#define EFI_CAPSULE_SUPPORTED_FLAG_MASK                        \
+       (EFI_CAPSULE_PERSIST_ACROSS_RESET | EFI_CAPSULE_POPULATE_SYSTEM_TABLE)
+
+/**
+ * efi_capsule_supported - does the firmware support the capsule?
+ * @guid: vendor guid of capsule
+ * @flags: capsule flags
+ * @size: size of capsule data
+ * @reset: the reset type required for this capsule
+ *
+ * Check whether a capsule with @flags is supported by the firmware
+ * and that @size doesn't exceed the maximum size for a capsule.
+ *
+ * No attempt is made to check @reset against the reset type required
+ * by any pending capsules because of the races involved.
+ */
+int efi_capsule_supported(efi_guid_t guid, u32 flags, size_t size, int *reset)
+{
+       efi_capsule_header_t capsule;
+       efi_capsule_header_t *cap_list[] = { &capsule };
+       efi_status_t status;
+       u64 max_size;
+
+       if (flags & ~EFI_CAPSULE_SUPPORTED_FLAG_MASK)
+               return -EINVAL;
+
+       capsule.headersize = capsule.imagesize = sizeof(capsule);
+       memcpy(&capsule.guid, &guid, sizeof(efi_guid_t));
+       capsule.flags = flags;
+
+       status = efi.query_capsule_caps(cap_list, 1, &max_size, reset);
+       if (status != EFI_SUCCESS)
+               return efi_status_to_err(status);
+
+       if (size > max_size)
+               return -ENOSPC;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(efi_capsule_supported);
+
+/*
+ * Every scatter gather list (block descriptor) page must end with a
+ * continuation pointer. The last continuation pointer of the last
+ * page must be zero to mark the end of the chain.
+ */
+#define SGLIST_PER_PAGE        ((PAGE_SIZE / sizeof(efi_capsule_block_desc_t)) - 1)
+
+/*
+ * How many scatter gather list (block descriptor) pages do we need
+ * to map @count pages?
+ */
+static inline unsigned int sg_pages_num(unsigned int count)
+{
+       return DIV_ROUND_UP(count, SGLIST_PER_PAGE);
+}
+
+/**
+ * efi_capsule_update_locked - pass a single capsule to the firmware
+ * @capsule: capsule to send to the firmware
+ * @sg_pages: array of scatter gather (block descriptor) pages
+ * @reset: the reset type required for @capsule
+ *
+ * Since this function must be called under capsule_mutex check
+ * whether efi_reset_type will conflict with @reset, and atomically
+ * set it and capsule_pending if a capsule was successfully sent to
+ * the firmware.
+ *
+ * We also check to see if the system is about to restart, and if so,
+ * abort. This avoids races between efi_capsule_update() and
+ * efi_capsule_pending().
+ */
+static int
+efi_capsule_update_locked(efi_capsule_header_t *capsule,
+                         struct page **sg_pages, int reset)
+{
+       efi_physical_addr_t sglist_phys;
+       efi_status_t status;
+
+       lockdep_assert_held(&capsule_mutex);
+
+       /*
+        * If someone has already registered a capsule that requires a
+        * different reset type, we're out of luck and must abort.
+        */
+       if (efi_reset_type >= 0 && efi_reset_type != reset) {
+               pr_err("Conflicting capsule reset type %d (%d).\n",
+                      reset, efi_reset_type);
+               return -EINVAL;
+       }
+
+       /*
+        * If the system is getting ready to restart it may have
+        * called efi_capsule_pending() to make decisions (such as
+        * whether to force an EFI reboot), and we're racing against
+        * that call. Abort in that case.
+        */
+       if (unlikely(stop_capsules)) {
+               pr_warn("Capsule update raced with reboot, aborting.\n");
+               return -EINVAL;
+       }
+
+       sglist_phys = page_to_phys(sg_pages[0]);
+
+       status = efi.update_capsule(&capsule, 1, sglist_phys);
+       if (status == EFI_SUCCESS) {
+               capsule_pending = true;
+               efi_reset_type = reset;
+       }
+
+       return efi_status_to_err(status);
+}
+
+/**
+ * efi_capsule_update - send a capsule to the firmware
+ * @capsule: capsule to send to firmware
+ * @pages: an array of capsule data pages
+ *
+ * Build a scatter gather list with EFI capsule block descriptors to
+ * map the capsule described by @capsule with its data in @pages and
+ * send it to the firmware via the UpdateCapsule() runtime service.
+ *
+ * @capsule must be a virtual mapping of the first page in @pages
+ * (@pages[0]) in the kernel address space. That is, a
+ * capsule_header_t that describes the entire contents of the capsule
+ * must be at the start of the first data page.
+ *
+ * Even though this function will validate that the firmware supports
+ * the capsule guid, users will likely want to check that
+ * efi_capsule_supported() returns true before calling this function
+ * because it makes it easier to print helpful error messages.
+ *
+ * If the capsule is successfully submitted to the firmware, any
+ * subsequent calls to efi_capsule_pending() will return true. @pages
+ * must not be released or modified if this function returns
+ * successfully.
+ *
+ * Callers must be prepared for this function to fail, which can
+ * happen if we raced with system reboot or if there is already a
+ * pending capsule that has a reset type that conflicts with the one
+ * required by @capsule. Do NOT use efi_capsule_pending() to detect
+ * this conflict since that would be racy. Instead, submit the capsule
+ * to efi_capsule_update() and check the return value.
+ *
+ * Return 0 on success, a converted EFI status code on failure.
+ */
+int efi_capsule_update(efi_capsule_header_t *capsule, struct page **pages)
+{
+       u32 imagesize = capsule->imagesize;
+       efi_guid_t guid = capsule->guid;
+       unsigned int count, sg_count;
+       u32 flags = capsule->flags;
+       struct page **sg_pages;
+       int rv, reset_type;
+       int i, j;
+
+       rv = efi_capsule_supported(guid, flags, imagesize, &reset_type);
+       if (rv)
+               return rv;
+
+       count = DIV_ROUND_UP(imagesize, PAGE_SIZE);
+       sg_count = sg_pages_num(count);
+
+       sg_pages = kzalloc(sg_count * sizeof(*sg_pages), GFP_KERNEL);
+       if (!sg_pages)
+               return -ENOMEM;
+
+       for (i = 0; i < sg_count; i++) {
+               sg_pages[i] = alloc_page(GFP_KERNEL);
+               if (!sg_pages[i]) {
+                       rv = -ENOMEM;
+                       goto out;
+               }
+       }
+
+       for (i = 0; i < sg_count; i++) {
+               efi_capsule_block_desc_t *sglist;
+
+               sglist = kmap(sg_pages[i]);
+               if (!sglist) {
+                       rv = -ENOMEM;
+                       goto out;
+               }
+
+               for (j = 0; j < SGLIST_PER_PAGE && count > 0; j++) {
+                       u64 sz = min_t(u64, imagesize, PAGE_SIZE);
+
+                       sglist[j].length = sz;
+                       sglist[j].data = page_to_phys(*pages++);
+
+                       imagesize -= sz;
+                       count--;
+               }
+
+               /* Continuation pointer */
+               sglist[j].length = 0;
+
+               if (i + 1 == sg_count)
+                       sglist[j].data = 0;
+               else
+                       sglist[j].data = page_to_phys(sg_pages[i + 1]);
+
+               kunmap(sg_pages[i]);
+       }
+
+       mutex_lock(&capsule_mutex);
+       rv = efi_capsule_update_locked(capsule, sg_pages, reset_type);
+       mutex_unlock(&capsule_mutex);
+
+out:
+       for (i = 0; rv && i < sg_count; i++) {
+               if (sg_pages[i])
+                       __free_page(sg_pages[i]);
+       }
+
+       kfree(sg_pages);
+       return rv;
+}
+EXPORT_SYMBOL_GPL(efi_capsule_update);
+
+static int capsule_reboot_notify(struct notifier_block *nb, unsigned long event, void *cmd)
+{
+       mutex_lock(&capsule_mutex);
+       stop_capsules = true;
+       mutex_unlock(&capsule_mutex);
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block capsule_reboot_nb = {
+       .notifier_call = capsule_reboot_notify,
+};
+
+static int __init capsule_reboot_register(void)
+{
+       return register_reboot_notifier(&capsule_reboot_nb);
+}
+core_initcall(capsule_reboot_register);
index 3a69ed5ecfcb5f1254eaf1b0c3fb53cbdbcc61fb..05509f3aaee8feab84d8cdfdf24d1bd4af4f3b77 100644 (file)
@@ -43,6 +43,7 @@ struct efi __read_mostly efi = {
        .config_table           = EFI_INVALID_TABLE_ADDR,
        .esrt                   = EFI_INVALID_TABLE_ADDR,
        .properties_table       = EFI_INVALID_TABLE_ADDR,
+       .mem_attr_table         = EFI_INVALID_TABLE_ADDR,
 };
 EXPORT_SYMBOL(efi);
 
@@ -256,7 +257,7 @@ subsys_initcall(efisubsys_init);
  */
 int __init efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md)
 {
-       struct efi_memory_map *map = efi.memmap;
+       struct efi_memory_map *map = &efi.memmap;
        phys_addr_t p, e;
 
        if (!efi_enabled(EFI_MEMMAP)) {
@@ -338,6 +339,7 @@ static __initdata efi_config_table_type_t common_tables[] = {
        {UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga},
        {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt},
        {EFI_PROPERTIES_TABLE_GUID, "PROP", &efi.properties_table},
+       {EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi.mem_attr_table},
        {NULL_GUID, NULL, NULL},
 };
 
@@ -351,8 +353,9 @@ static __init int match_config_table(efi_guid_t *guid,
                for (i = 0; efi_guidcmp(table_types[i].guid, NULL_GUID); i++) {
                        if (!efi_guidcmp(*guid, table_types[i].guid)) {
                                *(table_types[i].ptr) = table;
-                               pr_cont(" %s=0x%lx ",
-                                       table_types[i].name, table);
+                               if (table_types[i].name)
+                                       pr_cont(" %s=0x%lx ",
+                                               table_types[i].name, table);
                                return 1;
                        }
                }
@@ -620,16 +623,12 @@ char * __init efi_md_typeattr_format(char *buf, size_t size,
  */
 u64 __weak efi_mem_attributes(unsigned long phys_addr)
 {
-       struct efi_memory_map *map;
        efi_memory_desc_t *md;
-       void *p;
 
        if (!efi_enabled(EFI_MEMMAP))
                return 0;
 
-       map = efi.memmap;
-       for (p = map->map; p < map->map_end; p += map->desc_size) {
-               md = p;
+       for_each_efi_memory_desc(md) {
                if ((md->phys_addr <= phys_addr) &&
                    (phys_addr < (md->phys_addr +
                    (md->num_pages << EFI_PAGE_SHIFT))))
@@ -637,3 +636,36 @@ u64 __weak efi_mem_attributes(unsigned long phys_addr)
        }
        return 0;
 }
+
+int efi_status_to_err(efi_status_t status)
+{
+       int err;
+
+       switch (status) {
+       case EFI_SUCCESS:
+               err = 0;
+               break;
+       case EFI_INVALID_PARAMETER:
+               err = -EINVAL;
+               break;
+       case EFI_OUT_OF_RESOURCES:
+               err = -ENOSPC;
+               break;
+       case EFI_DEVICE_ERROR:
+               err = -EIO;
+               break;
+       case EFI_WRITE_PROTECTED:
+               err = -EROFS;
+               break;
+       case EFI_SECURITY_VIOLATION:
+               err = -EACCES;
+               break;
+       case EFI_NOT_FOUND:
+               err = -ENOENT;
+               break;
+       default:
+               err = -EINVAL;
+       }
+
+       return err;
+}
diff --git a/drivers/firmware/efi/efibc.c b/drivers/firmware/efi/efibc.c
new file mode 100644 (file)
index 0000000..8dd0c70
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * efibc: control EFI bootloaders which obey LoaderEntryOneShot var
+ * Copyright (c) 2013-2016, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ */
+
+#define pr_fmt(fmt) "efibc: " fmt
+
+#include <linux/efi.h>
+#include <linux/module.h>
+#include <linux/reboot.h>
+#include <linux/slab.h>
+
+static void efibc_str_to_str16(const char *str, efi_char16_t *str16)
+{
+       size_t i;
+
+       for (i = 0; i < strlen(str); i++)
+               str16[i] = str[i];
+
+       str16[i] = '\0';
+}
+
+static int efibc_set_variable(const char *name, const char *value)
+{
+       int ret;
+       efi_guid_t guid = LINUX_EFI_LOADER_ENTRY_GUID;
+       struct efivar_entry *entry;
+       size_t size = (strlen(value) + 1) * sizeof(efi_char16_t);
+
+       if (size > sizeof(entry->var.Data)) {
+               pr_err("value is too large");
+               return -EINVAL;
+       }
+
+       entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       if (!entry) {
+               pr_err("failed to allocate efivar entry");
+               return -ENOMEM;
+       }
+
+       efibc_str_to_str16(name, entry->var.VariableName);
+       efibc_str_to_str16(value, (efi_char16_t *)entry->var.Data);
+       memcpy(&entry->var.VendorGuid, &guid, sizeof(guid));
+
+       ret = efivar_entry_set(entry,
+                              EFI_VARIABLE_NON_VOLATILE
+                              | EFI_VARIABLE_BOOTSERVICE_ACCESS
+                              | EFI_VARIABLE_RUNTIME_ACCESS,
+                              size, entry->var.Data, NULL);
+       if (ret)
+               pr_err("failed to set %s EFI variable: 0x%x\n",
+                      name, ret);
+
+       kfree(entry);
+       return ret;
+}
+
+static int efibc_reboot_notifier_call(struct notifier_block *notifier,
+                                     unsigned long event, void *data)
+{
+       const char *reason = "shutdown";
+       int ret;
+
+       if (event == SYS_RESTART)
+               reason = "reboot";
+
+       ret = efibc_set_variable("LoaderEntryRebootReason", reason);
+       if (ret || !data)
+               return NOTIFY_DONE;
+
+       efibc_set_variable("LoaderEntryOneShot", (char *)data);
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block efibc_reboot_notifier = {
+       .notifier_call = efibc_reboot_notifier_call,
+};
+
+static int __init efibc_init(void)
+{
+       int ret;
+
+       if (!efi_enabled(EFI_RUNTIME_SERVICES))
+               return -ENODEV;
+
+       ret = register_reboot_notifier(&efibc_reboot_notifier);
+       if (ret)
+               pr_err("unable to register reboot notifier\n");
+
+       return ret;
+}
+module_init(efibc_init);
+
+static void __exit efibc_exit(void)
+{
+       unregister_reboot_notifier(&efibc_reboot_notifier);
+}
+module_exit(efibc_exit);
+
+MODULE_AUTHOR("Jeremy Compostella <jeremy.compostella@intel.com>");
+MODULE_AUTHOR("Matt Gumbel <matthew.k.gumbel@intel.com");
+MODULE_DESCRIPTION("EFI Bootloader Control");
+MODULE_LICENSE("GPL v2");
index 096adcbcb5a99720c77bc69af458bf5f9628dbd4..116b244dee68283016f7696ac6358eb800ea70a0 100644 (file)
@@ -661,7 +661,7 @@ static void efivar_update_sysfs_entries(struct work_struct *work)
                        return;
 
                err = efivar_init(efivar_update_sysfs_entry, entry,
-                                 true, false, &efivar_sysfs_list);
+                                 false, &efivar_sysfs_list);
                if (!err)
                        break;
 
@@ -730,8 +730,7 @@ int efivars_sysfs_init(void)
                return -ENOMEM;
        }
 
-       efivar_init(efivars_sysfs_callback, NULL, false,
-                   true, &efivar_sysfs_list);
+       efivar_init(efivars_sysfs_callback, NULL, true, &efivar_sysfs_list);
 
        error = create_efivars_bin_attributes();
        if (error) {
index ed3a854950cca80b0a29bb8a38362d61b083b701..48430aba13c189d3cad7f04c6e5ee036701f113c 100644 (file)
@@ -57,7 +57,7 @@ static int __init cmp_fake_mem(const void *x1, const void *x2)
 void __init efi_fake_memmap(void)
 {
        u64 start, end, m_start, m_end, m_attr;
-       int new_nr_map = memmap.nr_map;
+       int new_nr_map = efi.memmap.nr_map;
        efi_memory_desc_t *md;
        phys_addr_t new_memmap_phy;
        void *new_memmap;
@@ -68,8 +68,7 @@ void __init efi_fake_memmap(void)
                return;
 
        /* count up the number of EFI memory descriptor */
-       for (old = memmap.map; old < memmap.map_end; old += memmap.desc_size) {
-               md = old;
+       for_each_efi_memory_desc(md) {
                start = md->phys_addr;
                end = start + (md->num_pages << EFI_PAGE_SHIFT) - 1;
 
@@ -95,25 +94,25 @@ void __init efi_fake_memmap(void)
        }
 
        /* allocate memory for new EFI memmap */
-       new_memmap_phy = memblock_alloc(memmap.desc_size * new_nr_map,
+       new_memmap_phy = memblock_alloc(efi.memmap.desc_size * new_nr_map,
                                        PAGE_SIZE);
        if (!new_memmap_phy)
                return;
 
        /* create new EFI memmap */
        new_memmap = early_memremap(new_memmap_phy,
-                                   memmap.desc_size * new_nr_map);
+                                   efi.memmap.desc_size * new_nr_map);
        if (!new_memmap) {
-               memblock_free(new_memmap_phy, memmap.desc_size * new_nr_map);
+               memblock_free(new_memmap_phy, efi.memmap.desc_size * new_nr_map);
                return;
        }
 
-       for (old = memmap.map, new = new_memmap;
-            old < memmap.map_end;
-            old += memmap.desc_size, new += memmap.desc_size) {
+       for (old = efi.memmap.map, new = new_memmap;
+            old < efi.memmap.map_end;
+            old += efi.memmap.desc_size, new += efi.memmap.desc_size) {
 
                /* copy original EFI memory descriptor */
-               memcpy(new, old, memmap.desc_size);
+               memcpy(new, old, efi.memmap.desc_size);
                md = new;
                start = md->phys_addr;
                end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1;
@@ -134,8 +133,8 @@ void __init efi_fake_memmap(void)
                                md->num_pages = (m_end - md->phys_addr + 1) >>
                                        EFI_PAGE_SHIFT;
                                /* latter part */
-                               new += memmap.desc_size;
-                               memcpy(new, old, memmap.desc_size);
+                               new += efi.memmap.desc_size;
+                               memcpy(new, old, efi.memmap.desc_size);
                                md = new;
                                md->phys_addr = m_end + 1;
                                md->num_pages = (end - md->phys_addr + 1) >>
@@ -147,16 +146,16 @@ void __init efi_fake_memmap(void)
                                md->num_pages = (m_start - md->phys_addr) >>
                                        EFI_PAGE_SHIFT;
                                /* middle part */
-                               new += memmap.desc_size;
-                               memcpy(new, old, memmap.desc_size);
+                               new += efi.memmap.desc_size;
+                               memcpy(new, old, efi.memmap.desc_size);
                                md = new;
                                md->attribute |= m_attr;
                                md->phys_addr = m_start;
                                md->num_pages = (m_end - m_start + 1) >>
                                        EFI_PAGE_SHIFT;
                                /* last part */
-                               new += memmap.desc_size;
-                               memcpy(new, old, memmap.desc_size);
+                               new += efi.memmap.desc_size;
+                               memcpy(new, old, efi.memmap.desc_size);
                                md = new;
                                md->phys_addr = m_end + 1;
                                md->num_pages = (end - m_end) >>
@@ -169,8 +168,8 @@ void __init efi_fake_memmap(void)
                                md->num_pages = (m_start - md->phys_addr) >>
                                        EFI_PAGE_SHIFT;
                                /* latter part */
-                               new += memmap.desc_size;
-                               memcpy(new, old, memmap.desc_size);
+                               new += efi.memmap.desc_size;
+                               memcpy(new, old, efi.memmap.desc_size);
                                md = new;
                                md->phys_addr = m_start;
                                md->num_pages = (end - md->phys_addr + 1) >>
@@ -182,10 +181,10 @@ void __init efi_fake_memmap(void)
 
        /* swap into new EFI memmap */
        efi_unmap_memmap();
-       memmap.map = new_memmap;
-       memmap.phys_map = new_memmap_phy;
-       memmap.nr_map = new_nr_map;
-       memmap.map_end = memmap.map + memmap.nr_map * memmap.desc_size;
+       efi.memmap.map = new_memmap;
+       efi.memmap.phys_map = new_memmap_phy;
+       efi.memmap.nr_map = new_nr_map;
+       efi.memmap.map_end = efi.memmap.map + efi.memmap.nr_map * efi.memmap.desc_size;
        set_bit(EFI_MEMMAP, &efi.flags);
 
        /* print new EFI memmap */
index da99bbb74aebde62fc62128bed5d4ba2ee801212..c06945160a4154a5f8d518192b7adb16c4ab325d 100644 (file)
@@ -28,7 +28,7 @@ OBJECT_FILES_NON_STANDARD     := y
 # Prevents link failures: __sanitizer_cov_trace_pc() is not linked in.
 KCOV_INSTRUMENT                        := n
 
-lib-y                          := efi-stub-helper.o
+lib-y                          := efi-stub-helper.o gop.o
 
 # include the stub's generic dependencies from lib/ when building for ARM/arm64
 arm-deps := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c sort.c
index 414deb85c2e5214d4dc3a4cb85f4f8a20eb2df05..993aa56755f60afb929f90521485fd5fab335583 100644 (file)
 
 bool __nokaslr;
 
-static int efi_secureboot_enabled(efi_system_table_t *sys_table_arg)
+static int efi_get_secureboot(efi_system_table_t *sys_table_arg)
 {
-       static efi_guid_t const var_guid = EFI_GLOBAL_VARIABLE_GUID;
-       static efi_char16_t const var_name[] = {
+       static efi_char16_t const sb_var_name[] = {
                'S', 'e', 'c', 'u', 'r', 'e', 'B', 'o', 'o', 't', 0 };
+       static efi_char16_t const sm_var_name[] = {
+               'S', 'e', 't', 'u', 'p', 'M', 'o', 'd', 'e', 0 };
 
+       efi_guid_t var_guid = EFI_GLOBAL_VARIABLE_GUID;
        efi_get_variable_t *f_getvar = sys_table_arg->runtime->get_variable;
-       unsigned long size = sizeof(u8);
-       efi_status_t status;
        u8 val;
+       unsigned long size = sizeof(val);
+       efi_status_t status;
 
-       status = f_getvar((efi_char16_t *)var_name, (efi_guid_t *)&var_guid,
+       status = f_getvar((efi_char16_t *)sb_var_name, (efi_guid_t *)&var_guid,
                          NULL, &size, &val);
 
+       if (status != EFI_SUCCESS)
+               goto out_efi_err;
+
+       if (val == 0)
+               return 0;
+
+       status = f_getvar((efi_char16_t *)sm_var_name, (efi_guid_t *)&var_guid,
+                         NULL, &size, &val);
+
+       if (status != EFI_SUCCESS)
+               goto out_efi_err;
+
+       if (val == 1)
+               return 0;
+
+       return 1;
+
+out_efi_err:
        switch (status) {
-       case EFI_SUCCESS:
-               return val;
        case EFI_NOT_FOUND:
                return 0;
+       case EFI_DEVICE_ERROR:
+               return -EIO;
+       case EFI_SECURITY_VIOLATION:
+               return -EACCES;
        default:
-               return 1;
+               return -EINVAL;
        }
 }
 
@@ -147,6 +169,25 @@ void efi_char16_printk(efi_system_table_t *sys_table_arg,
        out->output_string(out, str);
 }
 
+static struct screen_info *setup_graphics(efi_system_table_t *sys_table_arg)
+{
+       efi_guid_t gop_proto = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
+       efi_status_t status;
+       unsigned long size;
+       void **gop_handle = NULL;
+       struct screen_info *si = NULL;
+
+       size = 0;
+       status = efi_call_early(locate_handle, EFI_LOCATE_BY_PROTOCOL,
+                               &gop_proto, NULL, &size, gop_handle);
+       if (status == EFI_BUFFER_TOO_SMALL) {
+               si = alloc_screen_info(sys_table_arg);
+               if (!si)
+                       return NULL;
+               efi_setup_gop(sys_table_arg, si, &gop_proto, size);
+       }
+       return si;
+}
 
 /*
  * This function handles the architcture specific differences between arm and
@@ -185,6 +226,8 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,
        efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID;
        unsigned long reserve_addr = 0;
        unsigned long reserve_size = 0;
+       int secure_boot = 0;
+       struct screen_info *si;
 
        /* Check if we were booted by the EFI firmware */
        if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
@@ -237,6 +280,8 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,
                        __nokaslr = true;
        }
 
+       si = setup_graphics(sys_table);
+
        status = handle_kernel_image(sys_table, image_addr, &image_size,
                                     &reserve_addr,
                                     &reserve_size,
@@ -250,12 +295,21 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,
        if (status != EFI_SUCCESS)
                pr_efi_err(sys_table, "Failed to parse EFI cmdline options\n");
 
+       secure_boot = efi_get_secureboot(sys_table);
+       if (secure_boot > 0)
+               pr_efi(sys_table, "UEFI Secure Boot is enabled.\n");
+
+       if (secure_boot < 0) {
+               pr_efi_err(sys_table,
+                       "could not determine UEFI Secure Boot status.\n");
+       }
+
        /*
         * Unauthenticated device tree data is a security hazard, so
         * ignore 'dtb=' unless UEFI Secure Boot is disabled.
         */
-       if (efi_secureboot_enabled(sys_table)) {
-               pr_efi(sys_table, "UEFI Secure Boot is enabled.\n");
+       if (secure_boot != 0 && strstr(cmdline_ptr, "dtb=")) {
+               pr_efi(sys_table, "Ignoring DTB from command line.\n");
        } else {
                status = handle_cmdline_files(sys_table, image, cmdline_ptr,
                                              "dtb=",
@@ -309,6 +363,7 @@ fail_free_image:
        efi_free(sys_table, image_size, *image_addr);
        efi_free(sys_table, reserve_size, reserve_addr);
 fail_free_cmdline:
+       free_screen_info(sys_table, si);
        efi_free(sys_table, cmdline_size, (unsigned long)cmdline_ptr);
 fail:
        return EFI_ERROR;
index 6f42be4d0084f1befcceb329758513b5f2c50781..e1f0b28e1dcbbf9cde9a1e882ba6212fb165c55e 100644 (file)
@@ -26,6 +26,43 @@ efi_status_t check_platform_features(efi_system_table_t *sys_table_arg)
        return EFI_SUCCESS;
 }
 
+static efi_guid_t screen_info_guid = LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID;
+
+struct screen_info *alloc_screen_info(efi_system_table_t *sys_table_arg)
+{
+       struct screen_info *si;
+       efi_status_t status;
+
+       /*
+        * Unlike on arm64, where we can directly fill out the screen_info
+        * structure from the stub, we need to allocate a buffer to hold
+        * its contents while we hand over to the kernel proper from the
+        * decompressor.
+        */
+       status = efi_call_early(allocate_pool, EFI_RUNTIME_SERVICES_DATA,
+                               sizeof(*si), (void **)&si);
+
+       if (status != EFI_SUCCESS)
+               return NULL;
+
+       status = efi_call_early(install_configuration_table,
+                               &screen_info_guid, si);
+       if (status == EFI_SUCCESS)
+               return si;
+
+       efi_call_early(free_pool, si);
+       return NULL;
+}
+
+void free_screen_info(efi_system_table_t *sys_table_arg, struct screen_info *si)
+{
+       if (!si)
+               return;
+
+       efi_call_early(install_configuration_table, &screen_info_guid, NULL);
+       efi_call_early(free_pool, si);
+}
+
 efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
                                 unsigned long *image_addr,
                                 unsigned long *image_size,
index 29ed2f9b218ca9892bfcc72da2d91ba4750f4c97..3bd127f953151dff89d0563db13deb12046e4315 100644 (file)
@@ -125,10 +125,12 @@ unsigned long get_dram_base(efi_system_table_t *sys_table_arg)
 
        map.map_end = map.map + map_size;
 
-       for_each_efi_memory_desc(&map, md)
-               if (md->attribute & EFI_MEMORY_WB)
+       for_each_efi_memory_desc_in_map(&map, md) {
+               if (md->attribute & EFI_MEMORY_WB) {
                        if (membase > md->phys_addr)
                                membase = md->phys_addr;
+               }
+       }
 
        efi_call_early(free_pool, map.map);
 
diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c
new file mode 100644 (file)
index 0000000..932742e
--- /dev/null
@@ -0,0 +1,354 @@
+/* -----------------------------------------------------------------------
+ *
+ *   Copyright 2011 Intel Corporation; author Matt Fleming
+ *
+ *   This file is part of the Linux kernel, and is made available under
+ *   the terms of the GNU General Public License version 2.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <linux/efi.h>
+#include <linux/screen_info.h>
+#include <asm/efi.h>
+#include <asm/setup.h>
+
+static void find_bits(unsigned long mask, u8 *pos, u8 *size)
+{
+       u8 first, len;
+
+       first = 0;
+       len = 0;
+
+       if (mask) {
+               while (!(mask & 0x1)) {
+                       mask = mask >> 1;
+                       first++;
+               }
+
+               while (mask & 0x1) {
+                       mask = mask >> 1;
+                       len++;
+               }
+       }
+
+       *pos = first;
+       *size = len;
+}
+
+static void
+setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
+                struct efi_pixel_bitmask pixel_info, int pixel_format)
+{
+       if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
+               si->lfb_depth = 32;
+               si->lfb_linelength = pixels_per_scan_line * 4;
+               si->red_size = 8;
+               si->red_pos = 0;
+               si->green_size = 8;
+               si->green_pos = 8;
+               si->blue_size = 8;
+               si->blue_pos = 16;
+               si->rsvd_size = 8;
+               si->rsvd_pos = 24;
+       } else if (pixel_format == PIXEL_BGR_RESERVED_8BIT_PER_COLOR) {
+               si->lfb_depth = 32;
+               si->lfb_linelength = pixels_per_scan_line * 4;
+               si->red_size = 8;
+               si->red_pos = 16;
+               si->green_size = 8;
+               si->green_pos = 8;
+               si->blue_size = 8;
+               si->blue_pos = 0;
+               si->rsvd_size = 8;
+               si->rsvd_pos = 24;
+       } else if (pixel_format == PIXEL_BIT_MASK) {
+               find_bits(pixel_info.red_mask, &si->red_pos, &si->red_size);
+               find_bits(pixel_info.green_mask, &si->green_pos,
+                         &si->green_size);
+               find_bits(pixel_info.blue_mask, &si->blue_pos, &si->blue_size);
+               find_bits(pixel_info.reserved_mask, &si->rsvd_pos,
+                         &si->rsvd_size);
+               si->lfb_depth = si->red_size + si->green_size +
+                       si->blue_size + si->rsvd_size;
+               si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
+       } else {
+               si->lfb_depth = 4;
+               si->lfb_linelength = si->lfb_width / 2;
+               si->red_size = 0;
+               si->red_pos = 0;
+               si->green_size = 0;
+               si->green_pos = 0;
+               si->blue_size = 0;
+               si->blue_pos = 0;
+               si->rsvd_size = 0;
+               si->rsvd_pos = 0;
+       }
+}
+
+static efi_status_t
+__gop_query32(efi_system_table_t *sys_table_arg,
+             struct efi_graphics_output_protocol_32 *gop32,
+             struct efi_graphics_output_mode_info **info,
+             unsigned long *size, u64 *fb_base)
+{
+       struct efi_graphics_output_protocol_mode_32 *mode;
+       efi_graphics_output_protocol_query_mode query_mode;
+       efi_status_t status;
+       unsigned long m;
+
+       m = gop32->mode;
+       mode = (struct efi_graphics_output_protocol_mode_32 *)m;
+       query_mode = (void *)(unsigned long)gop32->query_mode;
+
+       status = __efi_call_early(query_mode, (void *)gop32, mode->mode, size,
+                                 info);
+       if (status != EFI_SUCCESS)
+               return status;
+
+       *fb_base = mode->frame_buffer_base;
+       return status;
+}
+
+static efi_status_t
+setup_gop32(efi_system_table_t *sys_table_arg, struct screen_info *si,
+            efi_guid_t *proto, unsigned long size, void **gop_handle)
+{
+       struct efi_graphics_output_protocol_32 *gop32, *first_gop;
+       unsigned long nr_gops;
+       u16 width, height;
+       u32 pixels_per_scan_line;
+       u32 ext_lfb_base;
+       u64 fb_base;
+       struct efi_pixel_bitmask pixel_info;
+       int pixel_format;
+       efi_status_t status = EFI_NOT_FOUND;
+       u32 *handles = (u32 *)(unsigned long)gop_handle;
+       int i;
+
+       first_gop = NULL;
+       gop32 = NULL;
+
+       nr_gops = size / sizeof(u32);
+       for (i = 0; i < nr_gops; i++) {
+               struct efi_graphics_output_mode_info *info = NULL;
+               efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
+               bool conout_found = false;
+               void *dummy = NULL;
+               efi_handle_t h = (efi_handle_t)(unsigned long)handles[i];
+               u64 current_fb_base;
+
+               status = efi_call_early(handle_protocol, h,
+                                       proto, (void **)&gop32);
+               if (status != EFI_SUCCESS)
+                       continue;
+
+               status = efi_call_early(handle_protocol, h,
+                                       &conout_proto, &dummy);
+               if (status == EFI_SUCCESS)
+                       conout_found = true;
+
+               status = __gop_query32(sys_table_arg, gop32, &info, &size,
+                                      &current_fb_base);
+               if (status == EFI_SUCCESS && (!first_gop || conout_found)) {
+                       /*
+                        * Systems that use the UEFI Console Splitter may
+                        * provide multiple GOP devices, not all of which are
+                        * backed by real hardware. The workaround is to search
+                        * for a GOP implementing the ConOut protocol, and if
+                        * one isn't found, to just fall back to the first GOP.
+                        */
+                       width = info->horizontal_resolution;
+                       height = info->vertical_resolution;
+                       pixel_format = info->pixel_format;
+                       pixel_info = info->pixel_information;
+                       pixels_per_scan_line = info->pixels_per_scan_line;
+                       fb_base = current_fb_base;
+
+                       /*
+                        * Once we've found a GOP supporting ConOut,
+                        * don't bother looking any further.
+                        */
+                       first_gop = gop32;
+                       if (conout_found)
+                               break;
+               }
+       }
+
+       /* Did we find any GOPs? */
+       if (!first_gop)
+               goto out;
+
+       /* EFI framebuffer */
+       si->orig_video_isVGA = VIDEO_TYPE_EFI;
+
+       si->lfb_width = width;
+       si->lfb_height = height;
+       si->lfb_base = fb_base;
+
+       ext_lfb_base = (u64)(unsigned long)fb_base >> 32;
+       if (ext_lfb_base) {
+               si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
+               si->ext_lfb_base = ext_lfb_base;
+       }
+
+       si->pages = 1;
+
+       setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format);
+
+       si->lfb_size = si->lfb_linelength * si->lfb_height;
+
+       si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
+out:
+       return status;
+}
+
+static efi_status_t
+__gop_query64(efi_system_table_t *sys_table_arg,
+             struct efi_graphics_output_protocol_64 *gop64,
+             struct efi_graphics_output_mode_info **info,
+             unsigned long *size, u64 *fb_base)
+{
+       struct efi_graphics_output_protocol_mode_64 *mode;
+       efi_graphics_output_protocol_query_mode query_mode;
+       efi_status_t status;
+       unsigned long m;
+
+       m = gop64->mode;
+       mode = (struct efi_graphics_output_protocol_mode_64 *)m;
+       query_mode = (void *)(unsigned long)gop64->query_mode;
+
+       status = __efi_call_early(query_mode, (void *)gop64, mode->mode, size,
+                                 info);
+       if (status != EFI_SUCCESS)
+               return status;
+
+       *fb_base = mode->frame_buffer_base;
+       return status;
+}
+
+static efi_status_t
+setup_gop64(efi_system_table_t *sys_table_arg, struct screen_info *si,
+           efi_guid_t *proto, unsigned long size, void **gop_handle)
+{
+       struct efi_graphics_output_protocol_64 *gop64, *first_gop;
+       unsigned long nr_gops;
+       u16 width, height;
+       u32 pixels_per_scan_line;
+       u32 ext_lfb_base;
+       u64 fb_base;
+       struct efi_pixel_bitmask pixel_info;
+       int pixel_format;
+       efi_status_t status = EFI_NOT_FOUND;
+       u64 *handles = (u64 *)(unsigned long)gop_handle;
+       int i;
+
+       first_gop = NULL;
+       gop64 = NULL;
+
+       nr_gops = size / sizeof(u64);
+       for (i = 0; i < nr_gops; i++) {
+               struct efi_graphics_output_mode_info *info = NULL;
+               efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
+               bool conout_found = false;
+               void *dummy = NULL;
+               efi_handle_t h = (efi_handle_t)(unsigned long)handles[i];
+               u64 current_fb_base;
+
+               status = efi_call_early(handle_protocol, h,
+                                       proto, (void **)&gop64);
+               if (status != EFI_SUCCESS)
+                       continue;
+
+               status = efi_call_early(handle_protocol, h,
+                                       &conout_proto, &dummy);
+               if (status == EFI_SUCCESS)
+                       conout_found = true;
+
+               status = __gop_query64(sys_table_arg, gop64, &info, &size,
+                                      &current_fb_base);
+               if (status == EFI_SUCCESS && (!first_gop || conout_found)) {
+                       /*
+                        * Systems that use the UEFI Console Splitter may
+                        * provide multiple GOP devices, not all of which are
+                        * backed by real hardware. The workaround is to search
+                        * for a GOP implementing the ConOut protocol, and if
+                        * one isn't found, to just fall back to the first GOP.
+                        */
+                       width = info->horizontal_resolution;
+                       height = info->vertical_resolution;
+                       pixel_format = info->pixel_format;
+                       pixel_info = info->pixel_information;
+                       pixels_per_scan_line = info->pixels_per_scan_line;
+                       fb_base = current_fb_base;
+
+                       /*
+                        * Once we've found a GOP supporting ConOut,
+                        * don't bother looking any further.
+                        */
+                       first_gop = gop64;
+                       if (conout_found)
+                               break;
+               }
+       }
+
+       /* Did we find any GOPs? */
+       if (!first_gop)
+               goto out;
+
+       /* EFI framebuffer */
+       si->orig_video_isVGA = VIDEO_TYPE_EFI;
+
+       si->lfb_width = width;
+       si->lfb_height = height;
+       si->lfb_base = fb_base;
+
+       ext_lfb_base = (u64)(unsigned long)fb_base >> 32;
+       if (ext_lfb_base) {
+               si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
+               si->ext_lfb_base = ext_lfb_base;
+       }
+
+       si->pages = 1;
+
+       setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format);
+
+       si->lfb_size = si->lfb_linelength * si->lfb_height;
+
+       si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
+out:
+       return status;
+}
+
+/*
+ * See if we have Graphics Output Protocol
+ */
+efi_status_t efi_setup_gop(efi_system_table_t *sys_table_arg,
+                          struct screen_info *si, efi_guid_t *proto,
+                          unsigned long size)
+{
+       efi_status_t status;
+       void **gop_handle = NULL;
+
+       status = efi_call_early(allocate_pool, EFI_LOADER_DATA,
+                               size, (void **)&gop_handle);
+       if (status != EFI_SUCCESS)
+               return status;
+
+       status = efi_call_early(locate_handle,
+                               EFI_LOCATE_BY_PROTOCOL,
+                               proto, NULL, &size, gop_handle);
+       if (status != EFI_SUCCESS)
+               goto free_handle;
+
+       if (efi_is_64bit()) {
+               status = setup_gop64(sys_table_arg, si, proto, size,
+                                    gop_handle);
+       } else {
+               status = setup_gop32(sys_table_arg, si, proto, size,
+                                    gop_handle);
+       }
+
+free_handle:
+       efi_call_early(free_pool, gop_handle);
+       return status;
+}
diff --git a/drivers/firmware/efi/memattr.c b/drivers/firmware/efi/memattr.c
new file mode 100644 (file)
index 0000000..236004b
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2016 Linaro Ltd. <ard.biesheuvel@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt)    "efi: memattr: " fmt
+
+#include <linux/efi.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/memblock.h>
+
+#include <asm/early_ioremap.h>
+
+static int __initdata tbl_size;
+
+/*
+ * Reserve the memory associated with the Memory Attributes configuration
+ * table, if it exists.
+ */
+int __init efi_memattr_init(void)
+{
+       efi_memory_attributes_table_t *tbl;
+
+       if (efi.mem_attr_table == EFI_INVALID_TABLE_ADDR)
+               return 0;
+
+       tbl = early_memremap(efi.mem_attr_table, sizeof(*tbl));
+       if (!tbl) {
+               pr_err("Failed to map EFI Memory Attributes table @ 0x%lx\n",
+                      efi.mem_attr_table);
+               return -ENOMEM;
+       }
+
+       if (tbl->version > 1) {
+               pr_warn("Unexpected EFI Memory Attributes table version %d\n",
+                       tbl->version);
+               goto unmap;
+       }
+
+       tbl_size = sizeof(*tbl) + tbl->num_entries * tbl->desc_size;
+       memblock_reserve(efi.mem_attr_table, tbl_size);
+
+unmap:
+       early_memunmap(tbl, sizeof(*tbl));
+       return 0;
+}
+
+/*
+ * Returns a copy @out of the UEFI memory descriptor @in if it is covered
+ * entirely by a UEFI memory map entry with matching attributes. The virtual
+ * address of @out is set according to the matching entry that was found.
+ */
+static bool entry_is_valid(const efi_memory_desc_t *in, efi_memory_desc_t *out)
+{
+       u64 in_paddr = in->phys_addr;
+       u64 in_size = in->num_pages << EFI_PAGE_SHIFT;
+       efi_memory_desc_t *md;
+
+       *out = *in;
+
+       if (in->type != EFI_RUNTIME_SERVICES_CODE &&
+           in->type != EFI_RUNTIME_SERVICES_DATA) {
+               pr_warn("Entry type should be RuntimeServiceCode/Data\n");
+               return false;
+       }
+
+       if (!(in->attribute & (EFI_MEMORY_RO | EFI_MEMORY_XP))) {
+               pr_warn("Entry attributes invalid: RO and XP bits both cleared\n");
+               return false;
+       }
+
+       if (PAGE_SIZE > EFI_PAGE_SIZE &&
+           (!PAGE_ALIGNED(in->phys_addr) ||
+            !PAGE_ALIGNED(in->num_pages << EFI_PAGE_SHIFT))) {
+               /*
+                * Since arm64 may execute with page sizes of up to 64 KB, the
+                * UEFI spec mandates that RuntimeServices memory regions must
+                * be 64 KB aligned. We need to validate this here since we will
+                * not be able to tighten permissions on such regions without
+                * affecting adjacent regions.
+                */
+               pr_warn("Entry address region misaligned\n");
+               return false;
+       }
+
+       for_each_efi_memory_desc(md) {
+               u64 md_paddr = md->phys_addr;
+               u64 md_size = md->num_pages << EFI_PAGE_SHIFT;
+
+               if (!(md->attribute & EFI_MEMORY_RUNTIME))
+                       continue;
+               if (md->virt_addr == 0) {
+                       /* no virtual mapping has been installed by the stub */
+                       break;
+               }
+
+               if (md_paddr > in_paddr || (in_paddr - md_paddr) >= md_size)
+                       continue;
+
+               /*
+                * This entry covers the start of @in, check whether
+                * it covers the end as well.
+                */
+               if (md_paddr + md_size < in_paddr + in_size) {
+                       pr_warn("Entry covers multiple EFI memory map regions\n");
+                       return false;
+               }
+
+               if (md->type != in->type) {
+                       pr_warn("Entry type deviates from EFI memory map region type\n");
+                       return false;
+               }
+
+               out->virt_addr = in_paddr + (md->virt_addr - md_paddr);
+
+               return true;
+       }
+
+       pr_warn("No matching entry found in the EFI memory map\n");
+       return false;
+}
+
+/*
+ * To be called after the EFI page tables have been populated. If a memory
+ * attributes table is available, its contents will be used to update the
+ * mappings with tightened permissions as described by the table.
+ * This requires the UEFI memory map to have already been populated with
+ * virtual addresses.
+ */
+int __init efi_memattr_apply_permissions(struct mm_struct *mm,
+                                        efi_memattr_perm_setter fn)
+{
+       efi_memory_attributes_table_t *tbl;
+       int i, ret;
+
+       if (tbl_size <= sizeof(*tbl))
+               return 0;
+
+       /*
+        * We need the EFI memory map to be setup so we can use it to
+        * lookup the virtual addresses of all entries in the  of EFI
+        * Memory Attributes table. If it isn't available, this
+        * function should not be called.
+        */
+       if (WARN_ON(!efi_enabled(EFI_MEMMAP)))
+               return 0;
+
+       tbl = memremap(efi.mem_attr_table, tbl_size, MEMREMAP_WB);
+       if (!tbl) {
+               pr_err("Failed to map EFI Memory Attributes table @ 0x%lx\n",
+                      efi.mem_attr_table);
+               return -ENOMEM;
+       }
+
+       if (efi_enabled(EFI_DBG))
+               pr_info("Processing EFI Memory Attributes table:\n");
+
+       for (i = ret = 0; ret == 0 && i < tbl->num_entries; i++) {
+               efi_memory_desc_t md;
+               unsigned long size;
+               bool valid;
+               char buf[64];
+
+               valid = entry_is_valid((void *)tbl->entry + i * tbl->desc_size,
+                                      &md);
+               size = md.num_pages << EFI_PAGE_SHIFT;
+               if (efi_enabled(EFI_DBG) || !valid)
+                       pr_info("%s 0x%012llx-0x%012llx %s\n",
+                               valid ? "" : "!", md.phys_addr,
+                               md.phys_addr + size - 1,
+                               efi_md_typeattr_format(buf, sizeof(buf), &md));
+
+               if (valid)
+                       ret = fn(mm, &md);
+       }
+       memunmap(tbl);
+       return ret;
+}
index 9c59d1c795d1f2d6787b9a3a248f384e4017e8f1..62ead9b9d871a606af7c0b575f382377f7f82aaa 100644 (file)
@@ -9,7 +9,8 @@ int efi_reboot_quirk_mode = -1;
 
 void efi_reboot(enum reboot_mode reboot_mode, const char *__unused)
 {
-       int efi_mode;
+       const char *str[] = { "cold", "warm", "shutdown", "platform" };
+       int efi_mode, cap_reset_mode;
 
        if (!efi_enabled(EFI_RUNTIME_SERVICES))
                return;
@@ -30,6 +31,15 @@ void efi_reboot(enum reboot_mode reboot_mode, const char *__unused)
        if (efi_reboot_quirk_mode != -1)
                efi_mode = efi_reboot_quirk_mode;
 
+       if (efi_capsule_pending(&cap_reset_mode)) {
+               if (efi_mode != cap_reset_mode)
+                       printk(KERN_CRIT "efi: %s reset requested but pending "
+                              "capsule update requires %s reset... Performing "
+                              "%s reset.\n", str[efi_mode], str[cap_reset_mode],
+                              str[cap_reset_mode]);
+               efi_mode = cap_reset_mode;
+       }
+
        efi.reset_system(efi_mode, EFI_SUCCESS, 0, NULL);
 }
 
index de6953039af65255b1afe36d059fb2893fdcd6f9..23bef6bb73ee58db932e7fd34100e4d850e73a4a 100644 (file)
 
 #include <linux/bug.h>
 #include <linux/efi.h>
+#include <linux/irqflags.h>
 #include <linux/mutex.h>
 #include <linux/spinlock.h>
+#include <linux/stringify.h>
 #include <asm/efi.h>
 
+static void efi_call_virt_check_flags(unsigned long flags, const char *call)
+{
+       unsigned long cur_flags, mismatch;
+
+       local_save_flags(cur_flags);
+
+       mismatch = flags ^ cur_flags;
+       if (!WARN_ON_ONCE(mismatch & ARCH_EFI_IRQ_FLAGS_MASK))
+               return;
+
+       add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_NOW_UNRELIABLE);
+       pr_err_ratelimited(FW_BUG "IRQ flags corrupted (0x%08lx=>0x%08lx) by EFI %s\n",
+                          flags, cur_flags, call);
+       local_irq_restore(flags);
+}
+
+/*
+ * Arch code can implement the following three template macros, avoiding
+ * reptition for the void/non-void return cases of {__,}efi_call_virt:
+ *
+ *  * arch_efi_call_virt_setup
+ *
+ *    Sets up the environment for the call (e.g. switching page tables,
+ *    allowing kernel-mode use of floating point, if required).
+ *
+ *  * arch_efi_call_virt
+ *
+ *    Performs the call. The last expression in the macro must be the call
+ *    itself, allowing the logic to be shared by the void and non-void
+ *    cases.
+ *
+ *  * arch_efi_call_virt_teardown
+ *
+ *    Restores the usual kernel environment once the call has returned.
+ */
+
+#define efi_call_virt(f, args...)                                      \
+({                                                                     \
+       efi_status_t __s;                                               \
+       unsigned long flags;                                            \
+       arch_efi_call_virt_setup();                                     \
+       local_save_flags(flags);                                        \
+       __s = arch_efi_call_virt(f, args);                              \
+       efi_call_virt_check_flags(flags, __stringify(f));               \
+       arch_efi_call_virt_teardown();                                  \
+       __s;                                                            \
+})
+
+#define __efi_call_virt(f, args...)                                    \
+({                                                                     \
+       unsigned long flags;                                            \
+       arch_efi_call_virt_setup();                                     \
+       local_save_flags(flags);                                        \
+       arch_efi_call_virt(f, args);                                    \
+       efi_call_virt_check_flags(flags, __stringify(f));               \
+       arch_efi_call_virt_teardown();                                  \
+})
+
 /*
  * According to section 7.1 of the UEFI spec, Runtime Services are not fully
  * reentrant, and there are particular combinations of calls that need to be
index 34b741940494a24682e6cbf0e96d3246468a4fd6..d3b75138328665f0be09e2192199831473499502 100644 (file)
@@ -329,39 +329,6 @@ check_var_size_nonblocking(u32 attributes, unsigned long size)
        return fops->query_variable_store(attributes, size, true);
 }
 
-static int efi_status_to_err(efi_status_t status)
-{
-       int err;
-
-       switch (status) {
-       case EFI_SUCCESS:
-               err = 0;
-               break;
-       case EFI_INVALID_PARAMETER:
-               err = -EINVAL;
-               break;
-       case EFI_OUT_OF_RESOURCES:
-               err = -ENOSPC;
-               break;
-       case EFI_DEVICE_ERROR:
-               err = -EIO;
-               break;
-       case EFI_WRITE_PROTECTED:
-               err = -EROFS;
-               break;
-       case EFI_SECURITY_VIOLATION:
-               err = -EACCES;
-               break;
-       case EFI_NOT_FOUND:
-               err = -ENOENT;
-               break;
-       default:
-               err = -EINVAL;
-       }
-
-       return err;
-}
-
 static bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor,
                                struct list_head *head)
 {
@@ -452,8 +419,7 @@ static void dup_variable_bug(efi_char16_t *str16, efi_guid_t *vendor_guid,
  * Returns 0 on success, or a kernel error code on failure.
  */
 int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
-               void *data, bool atomic, bool duplicates,
-               struct list_head *head)
+               void *data, bool duplicates, struct list_head *head)
 {
        const struct efivar_operations *ops = __efivars->ops;
        unsigned long variable_name_size = 1024;
@@ -483,7 +449,7 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
                                                &vendor_guid);
                switch (status) {
                case EFI_SUCCESS:
-                       if (!atomic)
+                       if (duplicates)
                                spin_unlock_irq(&__efivars->lock);
 
                        variable_name_size = var_name_strnsize(variable_name,
@@ -498,21 +464,19 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
                         * and may end up looping here forever.
                         */
                        if (duplicates &&
-                           variable_is_present(variable_name, &vendor_guid, head)) {
+                           variable_is_present(variable_name, &vendor_guid,
+                                               head)) {
                                dup_variable_bug(variable_name, &vendor_guid,
                                                 variable_name_size);
-                               if (!atomic)
-                                       spin_lock_irq(&__efivars->lock);
-
                                status = EFI_NOT_FOUND;
-                               break;
+                       } else {
+                               err = func(variable_name, vendor_guid,
+                                          variable_name_size, data);
+                               if (err)
+                                       status = EFI_NOT_FOUND;
                        }
 
-                       err = func(variable_name, vendor_guid, variable_name_size, data);
-                       if (err)
-                               status = EFI_NOT_FOUND;
-
-                       if (!atomic)
+                       if (duplicates)
                                spin_lock_irq(&__efivars->lock);
 
                        break;
index 983280e8d93f3df4030c737978402ada4deaed10..e5a391aecde1b4c94a71181dacf665f99579bab1 100644 (file)
@@ -761,7 +761,7 @@ config FB_VESA
 
 config FB_EFI
        bool "EFI-based Framebuffer Support"
-       depends on (FB = y) && X86 && EFI
+       depends on (FB = y) && !IA64 && EFI
        select FB_CFB_FILLRECT
        select FB_CFB_COPYAREA
        select FB_CFB_IMAGEBLIT
index 95d293b7445a83473bc2131cf657f5de14ef52b3..f4c045c0051cc65fbcdb1df6207f4e3292b8c327 100644 (file)
@@ -6,16 +6,14 @@
  *
  */
 
-#include <linux/module.h>
 #include <linux/kernel.h>
+#include <linux/efi.h>
 #include <linux/errno.h>
 #include <linux/fb.h>
 #include <linux/platform_device.h>
 #include <linux/screen_info.h>
-#include <linux/dmi.h>
-#include <linux/pci.h>
 #include <video/vga.h>
-#include <asm/sysfb.h>
+#include <asm/efi.h>
 
 static bool request_mem_succeeded = false;
 
@@ -85,21 +83,13 @@ static struct fb_ops efifb_ops = {
 static int efifb_setup(char *options)
 {
        char *this_opt;
-       int i;
 
        if (options && *options) {
                while ((this_opt = strsep(&options, ",")) != NULL) {
                        if (!*this_opt) continue;
 
-                       for (i = 0; i < M_UNKNOWN; i++) {
-                               if (efifb_dmi_list[i].base != 0 &&
-                                   !strcmp(this_opt, efifb_dmi_list[i].optname)) {
-                                       screen_info.lfb_base = efifb_dmi_list[i].base;
-                                       screen_info.lfb_linelength = efifb_dmi_list[i].stride;
-                                       screen_info.lfb_width = efifb_dmi_list[i].width;
-                                       screen_info.lfb_height = efifb_dmi_list[i].height;
-                               }
-                       }
+                       efifb_setup_from_dmi(&screen_info, this_opt);
+
                        if (!strncmp(this_opt, "base:", 5))
                                screen_info.lfb_base = simple_strtoul(this_opt+5, NULL, 0);
                        else if (!strncmp(this_opt, "stride:", 7))
@@ -338,5 +328,4 @@ static struct platform_driver efifb_driver = {
        .remove = efifb_remove,
 };
 
-module_platform_driver(efifb_driver);
-MODULE_LICENSE("GPL");
+builtin_platform_driver(efifb_driver);
index be7e56a338e84dba95b9e42cddc7db3d15f224a8..e9d2135445c13a493f3193a2c2ccb545a2afbfab 100644 (file)
@@ -316,7 +316,6 @@ static const struct efi efi_xen __initconst = {
        .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. */
 };
 
index d48e0d261d78da6e1ce2830e65000e37ed4bd292..5f22e74bbadea0822e3f6f5378b732ab3c1e333d 100644 (file)
@@ -157,7 +157,7 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
        return 0;
 }
 
-long
+static long
 efivarfs_file_ioctl(struct file *file, unsigned int cmd, unsigned long p)
 {
        void __user *arg = (void __user *)p;
index 553c5d2db4a442757b1224d3d12b77d4bc20caee..9cb54a38832de664b3ced0c4b876ececb7c907dd 100644 (file)
@@ -216,8 +216,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
 
        INIT_LIST_HEAD(&efivarfs_list);
 
-       err = efivar_init(efivarfs_callback, (void *)sb, false,
-                         true, &efivarfs_list);
+       err = efivar_init(efivarfs_callback, (void *)sb, true, &efivarfs_list);
        if (err)
                __efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL, NULL);
 
index 1626474567ac503d26802fcefbd9f147605e4449..df7acb51f3cc7d26668fb9a751b52ea8b53acf5d 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/pfn.h>
 #include <linux/pstore.h>
 #include <linux/reboot.h>
+#include <linux/screen_info.h>
 
 #include <asm/page.h>
 
@@ -123,6 +124,13 @@ typedef struct {
        u32 imagesize;
 } efi_capsule_header_t;
 
+/*
+ * EFI capsule flags
+ */
+#define EFI_CAPSULE_PERSIST_ACROSS_RESET       0x00010000
+#define EFI_CAPSULE_POPULATE_SYSTEM_TABLE      0x00020000
+#define EFI_CAPSULE_INITIATE_RESET             0x00040000
+
 /*
  * Allocation types for calls to boottime->allocate_pages.
  */
@@ -282,9 +290,10 @@ typedef struct {
        efi_status_t (*handle_protocol)(efi_handle_t, efi_guid_t *, void **);
        void *__reserved;
        void *register_protocol_notify;
-       void *locate_handle;
+       efi_status_t (*locate_handle)(int, efi_guid_t *, void *,
+                                     unsigned long *, efi_handle_t *);
        void *locate_device_path;
-       void *install_configuration_table;
+       efi_status_t (*install_configuration_table)(efi_guid_t *, void *);
        void *load_image;
        void *start_image;
        void *exit;
@@ -623,6 +632,27 @@ void efi_native_runtime_setup(void);
        EFI_GUID(0x3152bca5, 0xeade, 0x433d, \
                 0x86, 0x2e, 0xc0, 0x1c, 0xdc, 0x29, 0x1f, 0x44)
 
+#define EFI_MEMORY_ATTRIBUTES_TABLE_GUID \
+       EFI_GUID(0xdcfa911d, 0x26eb, 0x469f, \
+                0xa2, 0x20, 0x38, 0xb7, 0xdc, 0x46, 0x12, 0x20)
+
+#define EFI_CONSOLE_OUT_DEVICE_GUID \
+       EFI_GUID(0xd3b36f2c, 0xd551, 0x11d4, \
+                0x9a, 0x46, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d)
+
+/*
+ * This GUID is used to pass to the kernel proper the struct screen_info
+ * structure that was populated by the stub based on the GOP protocol instance
+ * associated with ConOut
+ */
+#define LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID \
+       EFI_GUID(0xe03fc20a, 0x85dc, 0x406e, \
+                0xb9, 0xe, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95)
+
+#define LINUX_EFI_LOADER_ENTRY_GUID \
+       EFI_GUID(0x4a67b082, 0x0a4c, 0x41cf, \
+                0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f)
+
 typedef struct {
        efi_guid_t guid;
        u64 table;
@@ -847,6 +877,14 @@ typedef struct {
 
 #define EFI_INVALID_TABLE_ADDR         (~0UL)
 
+typedef struct {
+       u32 version;
+       u32 num_entries;
+       u32 desc_size;
+       u32 reserved;
+       efi_memory_desc_t entry[0];
+} efi_memory_attributes_table_t;
+
 /*
  * All runtime access to EFI goes through this structure:
  */
@@ -868,6 +906,7 @@ extern struct efi {
        unsigned long config_table;     /* config tables */
        unsigned long esrt;             /* ESRT table */
        unsigned long properties_table; /* properties table */
+       unsigned long mem_attr_table;   /* memory attributes table */
        efi_get_time_t *get_time;
        efi_set_time_t *set_time;
        efi_get_wakeup_time_t *get_wakeup_time;
@@ -883,7 +922,7 @@ extern struct efi {
        efi_get_next_high_mono_count_t *get_next_high_mono_count;
        efi_reset_system_t *reset_system;
        efi_set_virtual_address_map_t *set_virtual_address_map;
-       struct efi_memory_map *memmap;
+       struct efi_memory_map memmap;
        unsigned long flags;
 } efi;
 
@@ -945,7 +984,6 @@ extern void efi_initialize_iomem_resources(struct resource *code_resource,
 extern void efi_get_time(struct timespec *now);
 extern void efi_reserve_boot_services(void);
 extern int efi_get_fdt_params(struct efi_fdt_params *params);
-extern struct efi_memory_map memmap;
 extern struct kobject *efi_kobj;
 
 extern int efi_reboot_quirk_mode;
@@ -957,12 +995,34 @@ extern void __init efi_fake_memmap(void);
 static inline void efi_fake_memmap(void) { }
 #endif
 
+/*
+ * efi_memattr_perm_setter - arch specific callback function passed into
+ *                           efi_memattr_apply_permissions() that updates the
+ *                           mapping permissions described by the second
+ *                           argument in the page tables referred to by the
+ *                           first argument.
+ */
+typedef int (*efi_memattr_perm_setter)(struct mm_struct *, efi_memory_desc_t *);
+
+extern int efi_memattr_init(void);
+extern int efi_memattr_apply_permissions(struct mm_struct *mm,
+                                        efi_memattr_perm_setter fn);
+
 /* Iterate through an efi_memory_map */
-#define for_each_efi_memory_desc(m, md)                                           \
+#define for_each_efi_memory_desc_in_map(m, md)                            \
        for ((md) = (m)->map;                                              \
             (md) <= (efi_memory_desc_t *)((m)->map_end - (m)->desc_size); \
             (md) = (void *)(md) + (m)->desc_size)
 
+/**
+ * for_each_efi_memory_desc - iterate over descriptors in efi.memmap
+ * @md: the efi_memory_desc_t * iterator
+ *
+ * Once the loop finishes @md must not be accessed.
+ */
+#define for_each_efi_memory_desc(md) \
+       for_each_efi_memory_desc_in_map(&efi.memmap, md)
+
 /*
  * Format an EFI memory descriptor's type and attributes to a user-provided
  * character buffer, as per snprintf(), and return the buffer.
@@ -1000,7 +1060,6 @@ extern int __init efi_setup_pcdp_console(char *);
  * possible, remove EFI-related code altogether.
  */
 #define EFI_BOOT               0       /* Were we booted from EFI? */
-#define EFI_SYSTEM_TABLES      1       /* Can we use EFI system tables? */
 #define EFI_CONFIG_TABLES      2       /* Can we use EFI config tables? */
 #define EFI_RUNTIME_SERVICES   3       /* Can we use runtime services? */
 #define EFI_MEMMAP             4       /* Can we use EFI memory map? */
@@ -1026,8 +1085,16 @@ static inline bool efi_enabled(int feature)
 }
 static inline void
 efi_reboot(enum reboot_mode reboot_mode, const char *__unused) {}
+
+static inline bool
+efi_capsule_pending(int *reset_type)
+{
+       return false;
+}
 #endif
 
+extern int efi_status_to_err(efi_status_t status);
+
 /*
  * Variable Attributes
  */
@@ -1180,6 +1247,80 @@ struct efi_simple_text_output_protocol {
        void *test_string;
 };
 
+#define PIXEL_RGB_RESERVED_8BIT_PER_COLOR              0
+#define PIXEL_BGR_RESERVED_8BIT_PER_COLOR              1
+#define PIXEL_BIT_MASK                                 2
+#define PIXEL_BLT_ONLY                                 3
+#define PIXEL_FORMAT_MAX                               4
+
+struct efi_pixel_bitmask {
+       u32 red_mask;
+       u32 green_mask;
+       u32 blue_mask;
+       u32 reserved_mask;
+};
+
+struct efi_graphics_output_mode_info {
+       u32 version;
+       u32 horizontal_resolution;
+       u32 vertical_resolution;
+       int pixel_format;
+       struct efi_pixel_bitmask pixel_information;
+       u32 pixels_per_scan_line;
+} __packed;
+
+struct efi_graphics_output_protocol_mode_32 {
+       u32 max_mode;
+       u32 mode;
+       u32 info;
+       u32 size_of_info;
+       u64 frame_buffer_base;
+       u32 frame_buffer_size;
+} __packed;
+
+struct efi_graphics_output_protocol_mode_64 {
+       u32 max_mode;
+       u32 mode;
+       u64 info;
+       u64 size_of_info;
+       u64 frame_buffer_base;
+       u64 frame_buffer_size;
+} __packed;
+
+struct efi_graphics_output_protocol_mode {
+       u32 max_mode;
+       u32 mode;
+       unsigned long info;
+       unsigned long size_of_info;
+       u64 frame_buffer_base;
+       unsigned long frame_buffer_size;
+} __packed;
+
+struct efi_graphics_output_protocol_32 {
+       u32 query_mode;
+       u32 set_mode;
+       u32 blt;
+       u32 mode;
+};
+
+struct efi_graphics_output_protocol_64 {
+       u64 query_mode;
+       u64 set_mode;
+       u64 blt;
+       u64 mode;
+};
+
+struct efi_graphics_output_protocol {
+       unsigned long query_mode;
+       unsigned long set_mode;
+       unsigned long blt;
+       struct efi_graphics_output_protocol_mode *mode;
+};
+
+typedef efi_status_t (*efi_graphics_output_protocol_query_mode)(
+       struct efi_graphics_output_protocol *, u32, unsigned long *,
+       struct efi_graphics_output_mode_info **);
+
 extern struct list_head efivar_sysfs_list;
 
 static inline void
@@ -1195,8 +1336,7 @@ int efivars_unregister(struct efivars *efivars);
 struct kobject *efivars_kobject(void);
 
 int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
-               void *data, bool atomic, bool duplicates,
-               struct list_head *head);
+               void *data, bool duplicates, struct list_head *head);
 
 void efivar_entry_add(struct efivar_entry *entry, struct list_head *head);
 void efivar_entry_remove(struct efivar_entry *entry);
@@ -1242,6 +1382,13 @@ int efivars_sysfs_init(void);
 #define EFIVARS_DATA_SIZE_MAX 1024
 
 #endif /* CONFIG_EFI_VARS */
+extern bool efi_capsule_pending(int *reset_type);
+
+extern int efi_capsule_supported(efi_guid_t guid, u32 flags,
+                                size_t size, int *reset);
+
+extern int efi_capsule_update(efi_capsule_header_t *capsule,
+                             struct page **pages);
 
 #ifdef CONFIG_EFI_RUNTIME_MAP
 int efi_runtime_map_init(struct kobject *);
@@ -1319,5 +1466,9 @@ efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 
 efi_status_t efi_parse_options(char *cmdline);
 
+efi_status_t efi_setup_gop(efi_system_table_t *sys_table_arg,
+                          struct screen_info *si, efi_guid_t *proto,
+                          unsigned long size);
+
 bool efi_runtime_disabled(void);
 #endif /* _LINUX_EFI_H */