Date Mon, 16 May 2016 16:43:11 +0200 From Ingo Molnar <> Subject [GIT PULL] EFI changes for v4.7 Linus,



Please pull the latest efi-core-for-linus git tree from:



git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git efi-core-for-linus



# HEAD: 6c5450ef66816216e574885cf8d3ddb31ef77428 efivarfs: Make efivarfs_file_ioctl() static



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)



* ... other misc cleanups.



Thanks,



Ingo



------------------>

Ard Biesheuvel (19):

efi: Get rid of the EFI_SYSTEM_TABLES status bit

efi/arm*: Drop writable mapping of the UEFI System table

efi: Check EFI_MEMORY_DESCRIPTOR version explicitly

efi/arm*: Use memremap() to create the persistent memmap mapping

ARM/efi: Apply strict permissions for UEFI Runtime Services regions

arm64/efi: Apply strict permissions to UEFI Runtime Services regions

efi: Add support for the EFI_MEMORY_ATTRIBUTES_TABLE config table

efi: Implement generic support for the Memory Attributes table

efi/arm*: Take the Memory Attributes table into account

x86/efi: Prepare GOP handling code for reuse as generic code

efi/libstub: Move Graphics Output Protocol handling to generic code

x86/efi/efifb: Move DMI based quirks handling out of generic code

efifb: Use builtin_platform_driver and drop unused includes

arm64/efi/libstub: Make screen_info accessible to the UEFI stub

efi/arm/libstub: Make screen_info accessible to the UEFI stub

efi/arm*/libstub: Wire up GOP protocol to 'struct screen_info'

efi/arm*: Wire up 'struct screen_info' to efi-framebuffer platform device

efifb: Enable the efi-framebuffer platform driver for ARM and arm64

efi/arm-init: Reserve rather than unmap the memory map for ARM as well



Compostella, Jeremy (1):

efibc: Add EFI Bootloader Control module



Ingo Molnar (1):

efi: Remove unnecessary (and buggy) .memmap initialization from the Xen EFI driver



Jeremy Compostella (1):

efibc: Fix excessive stack footprint warning



Julia Lawall (1):

efi: Merge boolean flag arguments



Kweh, Hock Leong (1):

efi: Add misc char driver interface to update EFI firmware



Linn Crosetto (2):

efi/arm64: Report unexpected errors when determining Secure Boot status

efi/arm64: Check SetupMode when determining Secure Boot status



Mark Rutland (10):

efi/runtime-wrappers: Add {__,}efi_call_virt() templates

arm64/efi: Move to generic {__,}efi_call_virt()

arm/efi: Move to generic {__,}efi_call_virt()

x86/efi: Move to generic {__,}efi_call_virt()

efi/runtime-wrappers: Remove redundant #ifdefs

efi/runtime-wrappers: Detect firmware IRQ flag corruption

arm64/efi: Enable runtime call flag checking

arm/efi: Enable runtime call flag checking

x86/efi: Enable runtime call flag checking

efi/runtime-wrappers: Remove ARCH_EFI_IRQ_FLAGS_MASK #ifdef



Matt Fleming (9):

x86/mm/pat: Document the (currently) EFI-only code path

efi: Iterate over efi.memmap in for_each_efi_memory_desc()

efi: Remove global 'memmap' EFI memory map

x86/efi: Remove the always true EFI_DEBUG symbol

efi: Move efi_status_to_err() to drivers/firmware/efi/

efi: Add 'capsule' update support

x86/efi: Force EFI reboot to process pending capsules

efi/capsule: Make efi_capsule_pending() lockless

efi/capsule: Move 'capsule' to the stack in efi_capsule_supported()



Peter Jones (1):

efivarfs: Make efivarfs_file_ioctl() static





arch/arm/include/asm/efi.h | 37 +--

arch/arm/kernel/efi.c | 41 +++

arch/arm/kernel/setup.c | 3 +-

arch/arm64/include/asm/efi.h | 37 +--

arch/arm64/kernel/efi.c | 57 +++-

arch/arm64/kernel/image.h | 1 +

arch/ia64/kernel/efi.c | 2 -

arch/x86/boot/compressed/eboot.c | 308 +--------------------

arch/x86/boot/compressed/eboot.h | 74 ------

arch/x86/include/asm/efi.h | 52 ++--

arch/x86/kernel/reboot.c | 9 +

arch/x86/kernel/sysfb_efi.c | 15 ++

arch/x86/mm/pageattr.c | 8 +-

arch/x86/platform/efi/efi.c | 133 +++++-----

arch/x86/platform/efi/efi_64.c | 10 +-

arch/x86/platform/efi/quirks.c | 10 +-

drivers/firmware/efi/Kconfig | 25 ++

drivers/firmware/efi/Makefile | 5 +-

drivers/firmware/efi/arm-init.c | 96 +++++--

drivers/firmware/efi/arm-runtime.c | 45 ++--

drivers/firmware/efi/capsule-loader.c | 343 ++++++++++++++++++++++++

drivers/firmware/efi/capsule.c | 308 +++++++++++++++++++++

drivers/firmware/efi/efi.c | 48 +++-

drivers/firmware/efi/efibc.c | 113 ++++++++

drivers/firmware/efi/efivars.c | 5 +-

drivers/firmware/efi/fake_mem.c | 43 ++-

drivers/firmware/efi/libstub/Makefile | 2 +-

drivers/firmware/efi/libstub/arm-stub.c | 77 +++++-

drivers/firmware/efi/libstub/arm32-stub.c | 37 +++

drivers/firmware/efi/libstub/efi-stub-helper.c | 6 +-

drivers/firmware/efi/libstub/gop.c | 354 +++++++++++++++++++++++++

drivers/firmware/efi/memattr.c | 182 +++++++++++++

drivers/firmware/efi/reboot.c | 12 +-

drivers/firmware/efi/runtime-wrappers.c | 60 +++++

drivers/firmware/efi/vars.c | 56 +---

drivers/video/fbdev/Kconfig | 2 +-

drivers/video/fbdev/efifb.c | 21 +-

drivers/xen/efi.c | 1 -

fs/efivarfs/file.c | 2 +-

fs/efivarfs/super.c | 3 +-

include/linux/efi.h | 167 +++++++++++-

41 files changed, 2097 insertions(+), 713 deletions(-)

create mode 100644 drivers/firmware/efi/capsule-loader.c

create mode 100644 drivers/firmware/efi/capsule.c

create mode 100644 drivers/firmware/efi/efibc.c

create mode 100644 drivers/firmware/efi/libstub/gop.c

create mode 100644 drivers/firmware/efi/memattr.c



diff --git a/arch/arm/include/asm/efi.h b/arch/arm/include/asm/efi.h

index e0eea72deb87..a708fa1f0905 100644

--- a/arch/arm/include/asm/efi.h

+++ b/arch/arm/include/asm/efi.h

@@ -17,34 +17,28 @@

#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,

diff --git a/arch/arm/kernel/efi.c b/arch/arm/kernel/efi.c

index ff8a9d8acfac..9f43ba012d10 100644

--- a/arch/arm/kernel/efi.c

+++ b/arch/arm/kernel/efi.c

@@ -11,6 +11,41 @@

#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;

}

diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c

index 2c4bea39cf22..7d4e2850910c 100644

--- a/arch/arm/kernel/setup.c

+++ b/arch/arm/kernel/setup.c

@@ -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,

diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h

index 8e88a696c9cb..622db3c6474e 100644

--- a/arch/arm64/include/asm/efi.h

+++ b/arch/arm64/include/asm/efi.h

@@ -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



diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c

index b6abc852f2a1..78f52488f9ff 100644

--- a/arch/arm64/kernel/efi.c

+++ b/arch/arm64/kernel/efi.c

@@ -17,22 +17,51 @@



#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,

diff --git a/arch/arm64/kernel/image.h b/arch/arm64/kernel/image.h

index 5e360ce88f10..1428849aece8 100644

--- a/arch/arm64/kernel/image.h

+++ b/arch/arm64/kernel/image.h

@@ -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



diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c

index 300dac3702f1..bf0865cd438a 100644

--- a/arch/ia64/kernel/efi.c

+++ b/arch/ia64/kernel/efi.c

@@ -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)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c

index 583d539a4197..52fef606bc54 100644

--- a/arch/x86/boot/compressed/eboot.c

+++ b/arch/x86/boot/compressed/eboot.c

@@ -571,312 +571,6 @@ static void setup_efi_pci(struct boot_params *params)

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, ¤t_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, ¤t_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;

diff --git a/arch/x86/boot/compressed/eboot.h b/arch/x86/boot/compressed/eboot.h

index d487e727f1ec..c0223f1a89d7 100644

--- a/arch/x86/boot/compressed/eboot.h

+++ b/arch/x86/boot/compressed/eboot.h

@@ -11,80 +11,6 @@



#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;

diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h

index 53748c45e488..78d1e7467eae 100644

--- a/arch/x86/include/asm/efi.h

+++ b/arch/x86/include/asm/efi.h

@@ -3,6 +3,7 @@



#include <asm/fpu/api.h>

#include <asm/pgtable.h>

+#include <asm/processor-flags.h>

#include <asm/tlb.h>



/*

@@ -28,33 +29,22 @@



#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

diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c

index ab0adc0fa5db..a9b31eb815f2 100644

--- a/arch/x86/kernel/reboot.c

+++ b/arch/x86/kernel/reboot.c

@@ -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.

");

+ reboot_type = BOOT_EFI;

+ }

+

for (;;) {

/* Could also try the reset bit in the Hammer NB */

switch (reboot_type) {

diff --git a/arch/x86/kernel/sysfb_efi.c b/arch/x86/kernel/sysfb_efi.c

index 5da924bbf0a0..623965e86b65 100644

--- a/arch/x86/kernel/sysfb_efi.c

+++ b/arch/x86/kernel/sysfb_efi.c

@@ -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)) \

diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c

index 01be9ec3bf79..a1f0e1d0ddc2 100644

--- a/arch/x86/mm/pageattr.c

+++ b/arch/x86/mm/pageattr.c

@@ -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.

diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c

index 994a7df84a7b..f93545e7dc54 100644

--- a/arch/x86/platform/efi/efi.c

+++ b/arch/x86/platform/efi/efi.c

@@ -54,10 +54,6 @@

#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)

",

- 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!

");

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))))

diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c

index 49e4dd4a1f58..6e7242be1c87 100644

--- a/arch/x86/platform/efi/efi_64.c

+++ b/arch/x86/platform/efi/efi_64.c

@@ -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;

diff --git a/arch/x86/platform/efi/quirks.c b/arch/x86/platform/efi/quirks.c

index ab50ada1d56e..097cb09d917b 100644

--- a/arch/x86/platform/efi/quirks.c

+++ b/arch/x86/platform/efi/quirks.c

@@ -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;



diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig

index e1670d533f97..6394152f648f 100644

--- a/drivers/firmware/efi/Kconfig

+++ b/drivers/firmware/efi/Kconfig

@@ -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

diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile

index 62e654f255f4..a219640f881f 100644

--- a/drivers/firmware/efi/Makefile

+++ b/drivers/firmware/efi/Makefile

@@ -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

diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c

index 8714f8c271ba..ef90f0c4b70a 100644

--- a/drivers/firmware/efi/arm-init.c

+++ b/drivers/firmware/efi/arm-init.c

@@ -11,17 +11,19 @@

*

*/



+#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

");

+ 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:

");



- 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.

");

}

- 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);

diff --git a/drivers/firmware/efi/arm-runtime.c b/drivers/firmware/efi/arm-runtime.c

index 6ae21e41a429..17ccf0a8787a 100644

--- a/drivers/firmware/efi/arm-runtime.c

+++ b/drivers/firmware/efi/arm-runtime.c

@@ -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

");

+ 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.

");



- 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

");

- 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

");

+ efi.memmap.map = memremap(efi.memmap.phys_map, mapsize, MEMREMAP_WB);

+ if (!efi.memmap.map) {

+ pr_err("Failed to remap EFI memory map

");

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

");

+ pr_err("UEFI virtual mapping missing or invalid -- runtime services will not be available

");

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

index 000000000000..c99c24bc79b0

--- /dev/null

+++ b/drivers/firmware/efi/capsule-loader.c

@@ -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

", __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

",

+ __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

", __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

", __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

", __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'

",

+ __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

", __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

", __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

", __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

",

+ __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

", __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

",

+ __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

index 000000000000..53b9fd2293ee

--- /dev/null

+++ b/drivers/firmware/efi/capsule.c

@@ -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).

",

+ 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.

");

+ 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);

diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c

index 3a69ed5ecfcb..05509f3aaee8 100644

--- a/drivers/firmware/efi/efi.c

+++ b/drivers/firmware/efi/efi.c

@@ -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

index 000000000000..8dd0c7085e59

--- /dev/null

+++ b/drivers/firmware/efi/efibc.c

@@ -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

",

+ 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

");

+

+ 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");

diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c

index 096adcbcb5a9..116b244dee68 100644

--- a/drivers/firmware/efi/efivars.c

+++ b/drivers/firmware/efi/efivars.c

@@ -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) {

diff --git a/drivers/firmware/efi/fake_mem.c b/drivers/firmware/efi/fake_mem.c

index ed3a854950cc..48430aba13c1 100644

--- a/drivers/firmware/efi/fake_mem.c

+++ b/drivers/firmware/efi/fake_mem.c

@@ -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 */

diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile

index da99bbb74aeb..c06945160a41 100644

--- a/drivers/firmware/efi/libstub/Makefile

+++ b/drivers/firmware/efi/libstub/Makefile

@@ -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

diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c

index 414deb85c2e5..993aa56755f6 100644

--- a/drivers/firmware/efi/libstub/arm-stub.c

+++ b/drivers/firmware/efi/libstub/arm-stub.c

@@ -20,27 +20,49 @@



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

");



+ secure_boot = efi_get_secureboot(sys_table);

+ if (secure_boot > 0)

+ pr_efi(sys_table, "UEFI Secure Boot is enabled.

");

+

+ if (secure_boot < 0) {

+ pr_efi_err(sys_table,

+ "could not determine UEFI Secure Boot status.

");

+ }

+

/*

* 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.

");

+ if (secure_boot != 0 && strstr(cmdline_ptr, "dtb=")) {

+ pr_efi(sys_table, "Ignoring DTB from command line.

");

} else {

status = handle_cmdline_files(sys_table, image, cmdline_ptr,

"dtb=",

@@ -309,6 +363,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,

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;

diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c

index 6f42be4d0084..e1f0b28e1dcb 100644

--- a/drivers/firmware/efi/libstub/arm32-stub.c

+++ b/drivers/firmware/efi/libstub/arm32-stub.c

@@ -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,

diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c

index 29ed2f9b218c..3bd127f95315 100644

--- a/drivers/firmware/efi/libstub/efi-stub-helper.c

+++ b/drivers/firmware/efi/libstub/efi-stub-helper.c

@@ -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

index 000000000000..932742e4cf23

--- /dev/null

+++ b/drivers/firmware/efi/libstub/gop.c

@@ -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,

+ ¤t_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,

+ ¤t_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_P