Merge branch 'efi-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull EFI updates from Ingo Molnar:
 "The main changes in this cycle were:

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

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

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

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

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

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

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

   - Add Core EFI capsule support (Matt Fleming)

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

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

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

  ... and other misc cleanups"

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

View File

@ -17,33 +17,27 @@
#include <asm/mach/map.h> #include <asm/mach/map.h>
#include <asm/mmu_context.h> #include <asm/mmu_context.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/ptrace.h>
#ifdef CONFIG_EFI #ifdef CONFIG_EFI
void efi_init(void); void efi_init(void);
int efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md); 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, ...) \ #define arch_efi_call_virt_setup() efi_virtmap_load()
#define arch_efi_call_virt_teardown() efi_virtmap_unload()
#define arch_efi_call_virt(f, args...) \
({ \ ({ \
efi_##f##_t *__f; \ efi_##f##_t *__f; \
efi_status_t __s; \
\
efi_virtmap_load(); \
__f = efi.systab->runtime->f; \ __f = efi.systab->runtime->f; \
__s = __f(__VA_ARGS__); \ __f(args); \
efi_virtmap_unload(); \
__s; \
}) })
#define __efi_call_virt(f, ...) \ #define ARCH_EFI_IRQ_FLAGS_MASK \
({ \ (PSR_J_BIT | PSR_E_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT | \
efi_##f##_t *__f; \ PSR_T_BIT | MODE_MASK)
\
efi_virtmap_load(); \
__f = efi.systab->runtime->f; \
__f(__VA_ARGS__); \
efi_virtmap_unload(); \
})
static inline void efi_set_pgd(struct mm_struct *mm) static inline void efi_set_pgd(struct mm_struct *mm)
{ {
@ -59,7 +53,16 @@ void efi_virtmap_unload(void);
/* arch specific definitions used by the stub code */ /* 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, * A reasonable upper bound for the uncompressed kernel size is 32 MBytes,

View File

@ -11,6 +11,41 @@
#include <asm/mach/map.h> #include <asm/mach/map.h>
#include <asm/mmu_context.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) int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md)
{ {
struct map_desc desc = { 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; desc.type = MT_DEVICE;
create_mapping_late(mm, &desc, true); 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; return 0;
} }

View File

@ -883,7 +883,8 @@ static void __init request_standard_resources(const struct machine_desc *mdesc)
request_resource(&ioport_resource, &lp2); 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 = { struct screen_info screen_info = {
.orig_video_lines = 30, .orig_video_lines = 30,
.orig_video_cols = 80, .orig_video_cols = 80,

View File

@ -4,6 +4,7 @@
#include <asm/io.h> #include <asm/io.h>
#include <asm/mmu_context.h> #include <asm/mmu_context.h>
#include <asm/neon.h> #include <asm/neon.h>
#include <asm/ptrace.h>
#include <asm/tlbflush.h> #include <asm/tlbflush.h>
#ifdef CONFIG_EFI #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); 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(); \ kernel_neon_begin(); \
efi_virtmap_load(); \ 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; \ efi_##f##_t *__f; \
\
kernel_neon_begin(); \
efi_virtmap_load(); \
__f = efi.systab->runtime->f; \ __f = efi.systab->runtime->f; \
__f(__VA_ARGS__); \ __f(args); \
})
#define arch_efi_call_virt_teardown() \
({ \
efi_virtmap_unload(); \ efi_virtmap_unload(); \
kernel_neon_end(); \ 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 */ /* 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 EFI_FDT_ALIGN SZ_2M /* used by allocate_new_fdt_and_exit_boot() */
#define MAX_FDT_OFFSET SZ_512M #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 #define EFI_ALLOC_ALIGN SZ_64K

View File

@ -17,22 +17,51 @@
#include <asm/efi.h> #include <asm/efi.h>
/*
* 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)
{
u64 attr = md->attribute;
u32 type = md->type;
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) int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md)
{ {
pteval_t prot_val; pteval_t prot_val = create_mapping_protection(md);
/*
* 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);
create_pgd_mapping(mm, md->phys_addr, md->virt_addr, create_pgd_mapping(mm, md->phys_addr, md->virt_addr,
md->num_pages << EFI_PAGE_SHIFT, md->num_pages << EFI_PAGE_SHIFT,

View File

@ -112,6 +112,7 @@ __efistub___memset = KALLSYMS_HIDE(__pi_memset);
__efistub__text = KALLSYMS_HIDE(_text); __efistub__text = KALLSYMS_HIDE(_text);
__efistub__end = KALLSYMS_HIDE(_end); __efistub__end = KALLSYMS_HIDE(_end);
__efistub__edata = KALLSYMS_HIDE(_edata); __efistub__edata = KALLSYMS_HIDE(_edata);
__efistub_screen_info = KALLSYMS_HIDE(screen_info);
#endif #endif

View File

@ -531,8 +531,6 @@ efi_init (void)
efi.systab->hdr.revision >> 16, efi.systab->hdr.revision >> 16,
efi.systab->hdr.revision & 0xffff, vendor); efi.systab->hdr.revision & 0xffff, vendor);
set_bit(EFI_SYSTEM_TABLES, &efi.flags);
palo_phys = EFI_INVALID_TABLE_ADDR; palo_phys = EFI_INVALID_TABLE_ADDR;
if (efi_config_init(arch_tables) != 0) if (efi_config_init(arch_tables) != 0)

View File

@ -571,312 +571,6 @@ free_handle:
efi_call_early(free_pool, pci_handle); efi_call_early(free_pool, pci_handle);
} }
static void
setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
struct efi_pixel_bitmask pixel_info, int pixel_format)
{
if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
si->lfb_depth = 32;
si->lfb_linelength = pixels_per_scan_line * 4;
si->red_size = 8;
si->red_pos = 0;
si->green_size = 8;
si->green_pos = 8;
si->blue_size = 8;
si->blue_pos = 16;
si->rsvd_size = 8;
si->rsvd_pos = 24;
} else if (pixel_format == PIXEL_BGR_RESERVED_8BIT_PER_COLOR) {
si->lfb_depth = 32;
si->lfb_linelength = pixels_per_scan_line * 4;
si->red_size = 8;
si->red_pos = 16;
si->green_size = 8;
si->green_pos = 8;
si->blue_size = 8;
si->blue_pos = 0;
si->rsvd_size = 8;
si->rsvd_pos = 24;
} else if (pixel_format == PIXEL_BIT_MASK) {
find_bits(pixel_info.red_mask, &si->red_pos, &si->red_size);
find_bits(pixel_info.green_mask, &si->green_pos,
&si->green_size);
find_bits(pixel_info.blue_mask, &si->blue_pos, &si->blue_size);
find_bits(pixel_info.reserved_mask, &si->rsvd_pos,
&si->rsvd_size);
si->lfb_depth = si->red_size + si->green_size +
si->blue_size + si->rsvd_size;
si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
} else {
si->lfb_depth = 4;
si->lfb_linelength = si->lfb_width / 2;
si->red_size = 0;
si->red_pos = 0;
si->green_size = 0;
si->green_pos = 0;
si->blue_size = 0;
si->blue_pos = 0;
si->rsvd_size = 0;
si->rsvd_pos = 0;
}
}
static efi_status_t
__gop_query32(struct efi_graphics_output_protocol_32 *gop32,
struct efi_graphics_output_mode_info **info,
unsigned long *size, u64 *fb_base)
{
struct efi_graphics_output_protocol_mode_32 *mode;
efi_status_t status;
unsigned long m;
m = gop32->mode;
mode = (struct efi_graphics_output_protocol_mode_32 *)m;
status = efi_early->call(gop32->query_mode, gop32,
mode->mode, size, info);
if (status != EFI_SUCCESS)
return status;
*fb_base = mode->frame_buffer_base;
return status;
}
static efi_status_t
setup_gop32(struct screen_info *si, efi_guid_t *proto,
unsigned long size, void **gop_handle)
{
struct efi_graphics_output_protocol_32 *gop32, *first_gop;
unsigned long nr_gops;
u16 width, height;
u32 pixels_per_scan_line;
u32 ext_lfb_base;
u64 fb_base;
struct efi_pixel_bitmask pixel_info;
int pixel_format;
efi_status_t status;
u32 *handles = (u32 *)(unsigned long)gop_handle;
int i;
first_gop = NULL;
gop32 = NULL;
nr_gops = size / sizeof(u32);
for (i = 0; i < nr_gops; i++) {
struct efi_graphics_output_mode_info *info = NULL;
efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
bool conout_found = false;
void *dummy = NULL;
u32 h = handles[i];
u64 current_fb_base;
status = efi_call_early(handle_protocol, h,
proto, (void **)&gop32);
if (status != EFI_SUCCESS)
continue;
status = efi_call_early(handle_protocol, h,
&conout_proto, &dummy);
if (status == EFI_SUCCESS)
conout_found = true;
status = __gop_query32(gop32, &info, &size, &current_fb_base);
if (status == EFI_SUCCESS && (!first_gop || conout_found)) {
/*
* Systems that use the UEFI Console Splitter may
* provide multiple GOP devices, not all of which are
* backed by real hardware. The workaround is to search
* for a GOP implementing the ConOut protocol, and if
* one isn't found, to just fall back to the first GOP.
*/
width = info->horizontal_resolution;
height = info->vertical_resolution;
pixel_format = info->pixel_format;
pixel_info = info->pixel_information;
pixels_per_scan_line = info->pixels_per_scan_line;
fb_base = current_fb_base;
/*
* Once we've found a GOP supporting ConOut,
* don't bother looking any further.
*/
first_gop = gop32;
if (conout_found)
break;
}
}
/* Did we find any GOPs? */
if (!first_gop)
goto out;
/* EFI framebuffer */
si->orig_video_isVGA = VIDEO_TYPE_EFI;
si->lfb_width = width;
si->lfb_height = height;
si->lfb_base = fb_base;
ext_lfb_base = (u64)(unsigned long)fb_base >> 32;
if (ext_lfb_base) {
si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
si->ext_lfb_base = ext_lfb_base;
}
si->pages = 1;
setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format);
si->lfb_size = si->lfb_linelength * si->lfb_height;
si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
out:
return status;
}
static efi_status_t
__gop_query64(struct efi_graphics_output_protocol_64 *gop64,
struct efi_graphics_output_mode_info **info,
unsigned long *size, u64 *fb_base)
{
struct efi_graphics_output_protocol_mode_64 *mode;
efi_status_t status;
unsigned long m;
m = gop64->mode;
mode = (struct efi_graphics_output_protocol_mode_64 *)m;
status = efi_early->call(gop64->query_mode, gop64,
mode->mode, size, info);
if (status != EFI_SUCCESS)
return status;
*fb_base = mode->frame_buffer_base;
return status;
}
static efi_status_t
setup_gop64(struct screen_info *si, efi_guid_t *proto,
unsigned long size, void **gop_handle)
{
struct efi_graphics_output_protocol_64 *gop64, *first_gop;
unsigned long nr_gops;
u16 width, height;
u32 pixels_per_scan_line;
u32 ext_lfb_base;
u64 fb_base;
struct efi_pixel_bitmask pixel_info;
int pixel_format;
efi_status_t status;
u64 *handles = (u64 *)(unsigned long)gop_handle;
int i;
first_gop = NULL;
gop64 = NULL;
nr_gops = size / sizeof(u64);
for (i = 0; i < nr_gops; i++) {
struct efi_graphics_output_mode_info *info = NULL;
efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
bool conout_found = false;
void *dummy = NULL;
u64 h = handles[i];
u64 current_fb_base;
status = efi_call_early(handle_protocol, h,
proto, (void **)&gop64);
if (status != EFI_SUCCESS)
continue;
status = efi_call_early(handle_protocol, h,
&conout_proto, &dummy);
if (status == EFI_SUCCESS)
conout_found = true;
status = __gop_query64(gop64, &info, &size, &current_fb_base);
if (status == EFI_SUCCESS && (!first_gop || conout_found)) {
/*
* Systems that use the UEFI Console Splitter may
* provide multiple GOP devices, not all of which are
* backed by real hardware. The workaround is to search
* for a GOP implementing the ConOut protocol, and if
* one isn't found, to just fall back to the first GOP.
*/
width = info->horizontal_resolution;
height = info->vertical_resolution;
pixel_format = info->pixel_format;
pixel_info = info->pixel_information;
pixels_per_scan_line = info->pixels_per_scan_line;
fb_base = current_fb_base;
/*
* Once we've found a GOP supporting ConOut,
* don't bother looking any further.
*/
first_gop = gop64;
if (conout_found)
break;
}
}
/* Did we find any GOPs? */
if (!first_gop)
goto out;
/* EFI framebuffer */
si->orig_video_isVGA = VIDEO_TYPE_EFI;
si->lfb_width = width;
si->lfb_height = height;
si->lfb_base = fb_base;
ext_lfb_base = (u64)(unsigned long)fb_base >> 32;
if (ext_lfb_base) {
si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
si->ext_lfb_base = ext_lfb_base;
}
si->pages = 1;
setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format);
si->lfb_size = si->lfb_linelength * si->lfb_height;
si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
out:
return status;
}
/*
* See if we have Graphics Output Protocol
*/
static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
unsigned long size)
{
efi_status_t status;
void **gop_handle = NULL;
status = efi_call_early(allocate_pool, EFI_LOADER_DATA,
size, (void **)&gop_handle);
if (status != EFI_SUCCESS)
return status;
status = efi_call_early(locate_handle,
EFI_LOCATE_BY_PROTOCOL,
proto, NULL, &size, gop_handle);
if (status != EFI_SUCCESS)
goto free_handle;
if (efi_early->is64)
status = setup_gop64(si, proto, size, gop_handle);
else
status = setup_gop32(si, proto, size, gop_handle);
free_handle:
efi_call_early(free_pool, gop_handle);
return status;
}
static efi_status_t static efi_status_t
setup_uga32(void **uga_handle, unsigned long size, u32 *width, u32 *height) 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, EFI_LOCATE_BY_PROTOCOL,
&graphics_proto, NULL, &size, gop_handle); &graphics_proto, NULL, &size, gop_handle);
if (status == EFI_BUFFER_TOO_SMALL) 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) { if (status != EFI_SUCCESS) {
size = 0; size = 0;

View File

@ -11,80 +11,6 @@
#define DESC_TYPE_CODE_DATA (1 << 0) #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 { struct efi_uga_draw_protocol_32 {
u32 get_mode; u32 get_mode;
u32 set_mode; u32 set_mode;

View File

@ -3,6 +3,7 @@
#include <asm/fpu/api.h> #include <asm/fpu/api.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/processor-flags.h>
#include <asm/tlb.h> #include <asm/tlb.h>
/* /*
@ -28,33 +29,22 @@
#define MAX_CMDLINE_ADDRESS UINT_MAX #define MAX_CMDLINE_ADDRESS UINT_MAX
#define ARCH_EFI_IRQ_FLAGS_MASK X86_EFLAGS_IF
#ifdef CONFIG_X86_32 #ifdef CONFIG_X86_32
extern unsigned long asmlinkage efi_call_phys(void *, ...); 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. * Wrap all the virtual calls in a way that forces the parameters on the stack.
*/ */
#define arch_efi_call_virt(f, args...) \
/* Use this macro if your virtual returns a non-void value */
#define 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_##f##_t __attribute__((regparm(0)))*) \
efi.systab->runtime->f)(args); \ efi.systab->runtime->f)(args); \
kernel_fpu_end(); \
}) })
#define efi_ioremap(addr, size, type, attr) ioremap_cache(addr, size) #define efi_ioremap(addr, size, type, attr) ioremap_cache(addr, size)
@ -78,10 +68,8 @@ struct efi_scratch {
u64 phys_stack; u64 phys_stack;
} __packed; } __packed;
#define efi_call_virt(f, ...) \ #define arch_efi_call_virt_setup() \
({ \ ({ \
efi_status_t __s; \
\
efi_sync_low_kernel_mappings(); \ efi_sync_low_kernel_mappings(); \
preempt_disable(); \ preempt_disable(); \
__kernel_fpu_begin(); \ __kernel_fpu_begin(); \
@ -91,9 +79,13 @@ struct efi_scratch {
write_cr3((unsigned long)efi_scratch.efi_pgt); \ write_cr3((unsigned long)efi_scratch.efi_pgt); \
__flush_tlb_all(); \ __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) { \ if (efi_scratch.use_pgd) { \
write_cr3(efi_scratch.prev_cr3); \ write_cr3(efi_scratch.prev_cr3); \
__flush_tlb_all(); \ __flush_tlb_all(); \
@ -101,15 +93,8 @@ struct efi_scratch {
\ \
__kernel_fpu_end(); \ __kernel_fpu_end(); \
preempt_enable(); \ 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, extern void __iomem *__init efi_ioremap(unsigned long addr, unsigned long size,
u32 type, u64 attribute); u32 type, u64 attribute);
@ -180,6 +165,8 @@ static inline bool efi_runtime_supported(void)
extern struct console early_efi_console; extern struct console early_efi_console;
extern void parse_efi_setup(u64 phys_addr, u32 data_len); 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 #ifdef CONFIG_EFI_MIXED
extern void efi_thunk_runtime_setup(void); extern void efi_thunk_runtime_setup(void);
extern efi_status_t efi_thunk_set_virtual_address_map( 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, ...) \ #define efi_call_early(f, ...) \
__efi_early()->call(__efi_early()->f, __VA_ARGS__); __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); extern bool efi_reboot_required(void);
#else #else

View File

@ -535,6 +535,15 @@ static void native_machine_emergency_restart(void)
mode = reboot_mode == REBOOT_WARM ? 0x1234 : 0; mode = reboot_mode == REBOOT_WARM ? 0x1234 : 0;
*((unsigned short *)__va(0x472)) = mode; *((unsigned short *)__va(0x472)) = mode;
/*
* If an EFI capsule has been registered with the firmware then
* override the reboot= parameter.
*/
if (efi_capsule_pending(NULL)) {
pr_info("EFI capsule is pending, forcing EFI reboot.\n");
reboot_type = BOOT_EFI;
}
for (;;) { for (;;) {
/* Could also try the reset bit in the Hammer NB */ /* Could also try the reset bit in the Hammer NB */
switch (reboot_type) { switch (reboot_type) {

View File

@ -68,6 +68,21 @@ struct efifb_dmi_info efifb_dmi_list[] = {
[M_UNKNOWN] = { NULL, 0, 0, 0, 0, OVERRIDE_NONE } [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) ({ \ #define choose_value(dmivalue, fwvalue, field, flags) ({ \
typeof(fwvalue) _ret_ = fwvalue; \ typeof(fwvalue) _ret_ = fwvalue; \
if ((flags) & (field)) \ if ((flags) & (field)) \

View File

@ -1125,8 +1125,14 @@ static int populate_pgd(struct cpa_data *cpa, unsigned long addr)
static int __cpa_process_fault(struct cpa_data *cpa, unsigned long vaddr, static int __cpa_process_fault(struct cpa_data *cpa, unsigned long vaddr,
int primary) 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); return populate_pgd(cpa, vaddr);
}
/* /*
* Ignore all non primary paths. * Ignore all non primary paths.

View File

@ -54,10 +54,6 @@
#include <asm/rtc.h> #include <asm/rtc.h>
#include <asm/uv/uv.h> #include <asm/uv/uv.h>
#define EFI_DEBUG
struct efi_memory_map memmap;
static struct efi efi_phys __initdata; static struct efi efi_phys __initdata;
static efi_system_table_t efi_systab __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 __init efi_find_mirror(void)
{ {
void *p; efi_memory_desc_t *md;
u64 mirror_size = 0, total_size = 0; u64 mirror_size = 0, total_size = 0;
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { for_each_efi_memory_desc(md) {
efi_memory_desc_t *md = p;
unsigned long long start = md->phys_addr; unsigned long long start = md->phys_addr;
unsigned long long size = md->num_pages << EFI_PAGE_SHIFT; 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) 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) { for_each_efi_memory_desc(md) {
efi_memory_desc_t *md = p;
unsigned long long start = md->phys_addr; unsigned long long start = md->phys_addr;
unsigned long long size = md->num_pages << EFI_PAGE_SHIFT; unsigned long long size = md->num_pages << EFI_PAGE_SHIFT;
int e820_type; int e820_type;
@ -209,47 +203,47 @@ int __init efi_memblock_x86_reserve_range(void)
#else #else
pmap = (e->efi_memmap | ((__u64)e->efi_memmap_hi << 32)); pmap = (e->efi_memmap | ((__u64)e->efi_memmap_hi << 32));
#endif #endif
memmap.phys_map = pmap; efi.memmap.phys_map = pmap;
memmap.nr_map = e->efi_memmap_size / efi.memmap.nr_map = e->efi_memmap_size /
e->efi_memdesc_size; e->efi_memdesc_size;
memmap.desc_size = e->efi_memdesc_size; efi.memmap.desc_size = e->efi_memdesc_size;
memmap.desc_version = e->efi_memdesc_version; 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; return 0;
} }
void __init efi_print_memmap(void) void __init efi_print_memmap(void)
{ {
#ifdef EFI_DEBUG
efi_memory_desc_t *md; efi_memory_desc_t *md;
void *p; int i = 0;
int i;
for (p = memmap.map, i = 0; for_each_efi_memory_desc(md) {
p < memmap.map_end;
p += memmap.desc_size, i++) {
char buf[64]; char buf[64];
md = p;
pr_info("mem%02u: %s range=[0x%016llx-0x%016llx] (%lluMB)\n", pr_info("mem%02u: %s range=[0x%016llx-0x%016llx] (%lluMB)\n",
i, efi_md_typeattr_format(buf, sizeof(buf), md), i++, efi_md_typeattr_format(buf, sizeof(buf), md),
md->phys_addr, md->phys_addr,
md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1, md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1,
(md->num_pages >> (20 - EFI_PAGE_SHIFT))); (md->num_pages >> (20 - EFI_PAGE_SHIFT)));
} }
#endif /* EFI_DEBUG */
} }
void __init efi_unmap_memmap(void) void __init efi_unmap_memmap(void)
{ {
unsigned long size;
clear_bit(EFI_MEMMAP, &efi.flags); clear_bit(EFI_MEMMAP, &efi.flags);
if (memmap.map) {
early_memunmap(memmap.map, memmap.nr_map * memmap.desc_size); size = efi.memmap.nr_map * efi.memmap.desc_size;
memmap.map = NULL; 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 >> 16,
efi.systab->hdr.revision & 0xffff); efi.systab->hdr.revision & 0xffff);
set_bit(EFI_SYSTEM_TABLES, &efi.flags);
return 0; return 0;
} }
@ -440,17 +432,22 @@ static int __init efi_runtime_init(void)
static int __init efi_memmap_init(void) static int __init efi_memmap_init(void)
{ {
unsigned long addr, size;
if (efi_enabled(EFI_PARAVIRT)) if (efi_enabled(EFI_PARAVIRT))
return 0; return 0;
/* Map the EFI memory map */ /* Map the EFI memory map */
memmap.map = early_memremap((unsigned long)memmap.phys_map, size = efi.memmap.nr_map * efi.memmap.desc_size;
memmap.nr_map * memmap.desc_size); addr = (unsigned long)efi.memmap.phys_map;
if (memmap.map == NULL) {
efi.memmap.map = early_memremap(addr, size);
if (efi.memmap.map == NULL) {
pr_err("Could not map the memory map!\n"); pr_err("Could not map the memory map!\n");
return -ENOMEM; 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) if (add_efi_memmap)
do_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) void __init runtime_code_page_mkexec(void)
{ {
efi_memory_desc_t *md; efi_memory_desc_t *md;
void *p;
/* Make EFI runtime service code area executable */ /* Make EFI runtime service code area executable */
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { for_each_efi_memory_desc(md) {
md = p;
if (md->type != EFI_RUNTIME_SERVICES_CODE) if (md->type != EFI_RUNTIME_SERVICES_CODE)
continue; continue;
@ -604,12 +598,10 @@ void __init old_map_region(efi_memory_desc_t *md)
/* Merge contiguous regions of the same type and attribute */ /* Merge contiguous regions of the same type and attribute */
static void __init efi_merge_regions(void) static void __init efi_merge_regions(void)
{ {
void *p;
efi_memory_desc_t *md, *prev_md = NULL; 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; u64 prev_size;
md = p;
if (!prev_md) { if (!prev_md) {
prev_md = 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) static void __init save_runtime_map(void)
{ {
#ifdef CONFIG_KEXEC_CORE #ifdef CONFIG_KEXEC_CORE
unsigned long desc_size;
efi_memory_desc_t *md; efi_memory_desc_t *md;
void *tmp, *p, *q = NULL; void *tmp, *q = NULL;
int count = 0; int count = 0;
if (efi_enabled(EFI_OLD_MEMMAP)) if (efi_enabled(EFI_OLD_MEMMAP))
return; return;
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { desc_size = efi.memmap.desc_size;
md = p;
for_each_efi_memory_desc(md) {
if (!(md->attribute & EFI_MEMORY_RUNTIME) || if (!(md->attribute & EFI_MEMORY_RUNTIME) ||
(md->type == EFI_BOOT_SERVICES_CODE) || (md->type == EFI_BOOT_SERVICES_CODE) ||
(md->type == EFI_BOOT_SERVICES_DATA)) (md->type == EFI_BOOT_SERVICES_DATA))
continue; continue;
tmp = krealloc(q, (count + 1) * memmap.desc_size, GFP_KERNEL); tmp = krealloc(q, (count + 1) * desc_size, GFP_KERNEL);
if (!tmp) if (!tmp)
goto out; goto out;
q = tmp; q = tmp;
memcpy(q + count * memmap.desc_size, md, memmap.desc_size); memcpy(q + count * desc_size, md, desc_size);
count++; count++;
} }
efi_runtime_map_setup(q, count, memmap.desc_size); efi_runtime_map_setup(q, count, desc_size);
return; return;
out: out:
@ -714,10 +707,10 @@ static inline void *efi_map_next_entry_reverse(void *entry)
{ {
/* Initial call */ /* Initial call */
if (!entry) if (!entry)
return memmap.map_end - memmap.desc_size; return efi.memmap.map_end - efi.memmap.desc_size;
entry -= memmap.desc_size; entry -= efi.memmap.desc_size;
if (entry < memmap.map) if (entry < efi.memmap.map)
return NULL; return NULL;
return entry; return entry;
@ -759,10 +752,10 @@ static void *efi_map_next_entry(void *entry)
/* Initial call */ /* Initial call */
if (!entry) if (!entry)
return memmap.map; return efi.memmap.map;
entry += memmap.desc_size; entry += efi.memmap.desc_size;
if (entry >= memmap.map_end) if (entry >= efi.memmap.map_end)
return NULL; return NULL;
return entry; return entry;
@ -776,8 +769,11 @@ static void * __init efi_map_regions(int *count, int *pg_shift)
{ {
void *p, *new_memmap = NULL; void *p, *new_memmap = NULL;
unsigned long left = 0; unsigned long left = 0;
unsigned long desc_size;
efi_memory_desc_t *md; efi_memory_desc_t *md;
desc_size = efi.memmap.desc_size;
p = NULL; p = NULL;
while ((p = efi_map_next_entry(p))) { while ((p = efi_map_next_entry(p))) {
md = p; md = p;
@ -792,7 +788,7 @@ static void * __init efi_map_regions(int *count, int *pg_shift)
efi_map_region(md); efi_map_region(md);
get_systab_virt_addr(md); get_systab_virt_addr(md);
if (left < memmap.desc_size) { if (left < desc_size) {
new_memmap = realloc_pages(new_memmap, *pg_shift); new_memmap = realloc_pages(new_memmap, *pg_shift);
if (!new_memmap) if (!new_memmap)
return NULL; return NULL;
@ -801,10 +797,9 @@ static void * __init efi_map_regions(int *count, int *pg_shift)
(*pg_shift)++; (*pg_shift)++;
} }
memcpy(new_memmap + (*count * memmap.desc_size), md, memcpy(new_memmap + (*count * desc_size), md, desc_size);
memmap.desc_size);
left -= memmap.desc_size; left -= desc_size;
(*count)++; (*count)++;
} }
@ -816,7 +811,6 @@ static void __init kexec_enter_virtual_mode(void)
#ifdef CONFIG_KEXEC_CORE #ifdef CONFIG_KEXEC_CORE
efi_memory_desc_t *md; efi_memory_desc_t *md;
unsigned int num_pages; unsigned int num_pages;
void *p;
efi.systab = NULL; 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 * 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. * fixed addr which was used in first kernel of a kexec boot.
*/ */
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { for_each_efi_memory_desc(md) {
md = p;
efi_map_region_fixed(md); /* FIXME: add error handling */ efi_map_region_fixed(md); /* FIXME: add error handling */
get_systab_virt_addr(md); get_systab_virt_addr(md);
} }
@ -850,10 +843,10 @@ static void __init kexec_enter_virtual_mode(void)
BUG_ON(!efi.systab); 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; 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); clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
return; return;
} }
@ -937,16 +930,16 @@ static void __init __efi_enter_virtual_mode(void)
if (efi_is_native()) { if (efi_is_native()) {
status = phys_efi_set_virtual_address_map( status = phys_efi_set_virtual_address_map(
memmap.desc_size * count, efi.memmap.desc_size * count,
memmap.desc_size, efi.memmap.desc_size,
memmap.desc_version, efi.memmap.desc_version,
(efi_memory_desc_t *)__pa(new_memmap)); (efi_memory_desc_t *)__pa(new_memmap));
} else { } else {
status = efi_thunk_set_virtual_address_map( status = efi_thunk_set_virtual_address_map(
efi_phys.set_virtual_address_map, efi_phys.set_virtual_address_map,
memmap.desc_size * count, efi.memmap.desc_size * count,
memmap.desc_size, efi.memmap.desc_size,
memmap.desc_version, efi.memmap.desc_version,
(efi_memory_desc_t *)__pa(new_memmap)); (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) u32 efi_mem_type(unsigned long phys_addr)
{ {
efi_memory_desc_t *md; efi_memory_desc_t *md;
void *p;
if (!efi_enabled(EFI_MEMMAP)) if (!efi_enabled(EFI_MEMMAP))
return 0; return 0;
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { for_each_efi_memory_desc(md) {
md = p;
if ((md->phys_addr <= phys_addr) && if ((md->phys_addr <= phys_addr) &&
(phys_addr < (md->phys_addr + (phys_addr < (md->phys_addr +
(md->num_pages << EFI_PAGE_SHIFT)))) (md->num_pages << EFI_PAGE_SHIFT))))

View File

@ -55,14 +55,12 @@ struct efi_scratch efi_scratch;
static void __init early_code_mapping_set_exec(int executable) static void __init early_code_mapping_set_exec(int executable)
{ {
efi_memory_desc_t *md; efi_memory_desc_t *md;
void *p;
if (!(__supported_pte_mask & _PAGE_NX)) if (!(__supported_pte_mask & _PAGE_NX))
return; return;
/* Make EFI service code area executable */ /* Make EFI service code area executable */
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { for_each_efi_memory_desc(md) {
md = p;
if (md->type == EFI_RUNTIME_SERVICES_CODE || if (md->type == EFI_RUNTIME_SERVICES_CODE ||
md->type == EFI_BOOT_SERVICES_CODE) md->type == EFI_BOOT_SERVICES_CODE)
efi_set_executable(md, executable); 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 * Map all of RAM so that we can access arguments in the 1:1
* mapping when making EFI runtime calls. * 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 && if (md->type != EFI_CONVENTIONAL_MEMORY &&
md->type != EFI_LOADER_DATA && md->type != EFI_LOADER_DATA &&
md->type != EFI_LOADER_CODE) md->type != EFI_LOADER_CODE)
@ -398,7 +396,6 @@ void __init efi_runtime_update_mappings(void)
unsigned long pfn; unsigned long pfn;
pgd_t *pgd = efi_pgd; pgd_t *pgd = efi_pgd;
efi_memory_desc_t *md; efi_memory_desc_t *md;
void *p;
if (efi_enabled(EFI_OLD_MEMMAP)) { if (efi_enabled(EFI_OLD_MEMMAP)) {
if (__supported_pte_mask & _PAGE_NX) if (__supported_pte_mask & _PAGE_NX)
@ -409,9 +406,8 @@ void __init efi_runtime_update_mappings(void)
if (!efi_enabled(EFI_NX_PE_DATA)) if (!efi_enabled(EFI_NX_PE_DATA))
return; return;
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { for_each_efi_memory_desc(md) {
unsigned long pf = 0; unsigned long pf = 0;
md = p;
if (!(md->attribute & EFI_MEMORY_RUNTIME)) if (!(md->attribute & EFI_MEMORY_RUNTIME))
continue; continue;

View File

@ -195,10 +195,9 @@ static bool can_free_region(u64 start, u64 size)
*/ */
void __init efi_reserve_boot_services(void) 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) { for_each_efi_memory_desc(md) {
efi_memory_desc_t *md = p;
u64 start = md->phys_addr; u64 start = md->phys_addr;
u64 size = md->num_pages << EFI_PAGE_SHIFT; u64 size = md->num_pages << EFI_PAGE_SHIFT;
bool already_reserved; bool already_reserved;
@ -250,10 +249,9 @@ void __init efi_reserve_boot_services(void)
void __init efi_free_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) { for_each_efi_memory_desc(md) {
efi_memory_desc_t *md = p;
unsigned long long start = md->phys_addr; unsigned long long start = md->phys_addr;
unsigned long long size = md->num_pages << EFI_PAGE_SHIFT; unsigned long long size = md->num_pages << EFI_PAGE_SHIFT;

View File

@ -87,6 +87,31 @@ config EFI_RUNTIME_WRAPPERS
config EFI_ARMSTUB config EFI_ARMSTUB
bool 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 endmenu
config UEFI_CPER config UEFI_CPER

View File

@ -9,7 +9,8 @@
# #
KASAN_SANITIZE_runtime-wrappers.o := n 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_VARS) += efivars.o
obj-$(CONFIG_EFI_ESRT) += esrt.o obj-$(CONFIG_EFI_ESRT) += esrt.o
obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.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_RUNTIME_WRAPPERS) += runtime-wrappers.o
obj-$(CONFIG_EFI_STUB) += libstub/ obj-$(CONFIG_EFI_STUB) += libstub/
obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_mem.o 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 arm-obj-$(CONFIG_EFI) := arm-init.o arm-runtime.o
obj-$(CONFIG_ARM) += $(arm-obj-y) obj-$(CONFIG_ARM) += $(arm-obj-y)
obj-$(CONFIG_ARM64) += $(arm-obj-y) obj-$(CONFIG_ARM64) += $(arm-obj-y)
obj-$(CONFIG_EFI_CAPSULE_LOADER) += capsule-loader.o

View File

@ -11,17 +11,19 @@
* *
*/ */
#define pr_fmt(fmt) "efi: " fmt
#include <linux/efi.h> #include <linux/efi.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/memblock.h> #include <linux/memblock.h>
#include <linux/mm_types.h> #include <linux/mm_types.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_fdt.h> #include <linux/of_fdt.h>
#include <linux/platform_device.h>
#include <linux/screen_info.h>
#include <asm/efi.h> #include <asm/efi.h>
struct efi_memory_map memmap;
u64 efi_system_table; u64 efi_system_table;
static int __init is_normal_ram(efi_memory_desc_t *md) 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; efi_memory_desc_t *md;
for_each_efi_memory_desc(&memmap, md) { for_each_efi_memory_desc(md) {
if (!(md->attribute & EFI_MEMORY_RUNTIME)) if (!(md->attribute & EFI_MEMORY_RUNTIME))
continue; continue;
if (md->virt_addr == 0) if (md->virt_addr == 0)
@ -53,6 +55,36 @@ static phys_addr_t efi_to_phys(unsigned long addr)
return addr; return addr;
} }
static __initdata unsigned long screen_info_table = EFI_INVALID_TABLE_ADDR;
static __initdata efi_config_table_type_t arch_tables[] = {
{LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, NULL, &screen_info_table},
{NULL_GUID, NULL, NULL}
};
static void __init init_screen_info(void)
{
struct screen_info *si;
if (screen_info_table != EFI_INVALID_TABLE_ADDR) {
si = early_memremap_ro(screen_info_table, sizeof(*si));
if (!si) {
pr_err("Could not map screen_info config table\n");
return;
}
screen_info = *si;
early_memunmap(si, sizeof(*si));
/* dummycon on ARM needs non-zero values for columns/lines */
screen_info.orig_video_cols = 80;
screen_info.orig_video_lines = 25;
}
if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI &&
memblock_is_map_memory(screen_info.lfb_base))
memblock_mark_nomap(screen_info.lfb_base, screen_info.lfb_size);
}
static int __init uefi_init(void) static int __init uefi_init(void)
{ {
efi_char16_t *c16; efi_char16_t *c16;
@ -85,6 +117,8 @@ static int __init uefi_init(void)
efi.systab->hdr.revision >> 16, efi.systab->hdr.revision >> 16,
efi.systab->hdr.revision & 0xffff); efi.systab->hdr.revision & 0xffff);
efi.runtime_version = efi.systab->hdr.revision;
/* Show what we know for posterity */ /* Show what we know for posterity */
c16 = early_memremap_ro(efi_to_phys(efi.systab->fw_vendor), c16 = early_memremap_ro(efi_to_phys(efi.systab->fw_vendor),
sizeof(vendor) * sizeof(efi_char16_t)); sizeof(vendor) * sizeof(efi_char16_t));
@ -108,7 +142,8 @@ static int __init uefi_init(void)
goto out; goto out;
} }
retval = efi_config_parse_tables(config_tables, efi.systab->nr_tables, 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); early_memunmap(config_tables, table_size);
out: out:
@ -143,7 +178,7 @@ static __init void reserve_regions(void)
if (efi_enabled(EFI_DBG)) if (efi_enabled(EFI_DBG))
pr_info("Processing EFI memory map:\n"); pr_info("Processing EFI memory map:\n");
for_each_efi_memory_desc(&memmap, md) { for_each_efi_memory_desc(md) {
paddr = md->phys_addr; paddr = md->phys_addr;
npages = md->num_pages; npages = md->num_pages;
@ -184,9 +219,9 @@ void __init efi_init(void)
efi_system_table = params.system_table; efi_system_table = params.system_table;
memmap.phys_map = params.mmap; efi.memmap.phys_map = params.mmap;
memmap.map = early_memremap_ro(params.mmap, params.mmap_size); efi.memmap.map = early_memremap_ro(params.mmap, params.mmap_size);
if (memmap.map == NULL) { if (efi.memmap.map == NULL) {
/* /*
* If we are booting via UEFI, the UEFI memory map is the only * If we are booting via UEFI, the UEFI memory map is the only
* description of memory we have, so there is little point in * description of memory we have, so there is little point in
@ -194,28 +229,37 @@ void __init efi_init(void)
*/ */
panic("Unable to map EFI memory map.\n"); panic("Unable to map EFI memory map.\n");
} }
memmap.map_end = memmap.map + params.mmap_size; efi.memmap.map_end = efi.memmap.map + params.mmap_size;
memmap.desc_size = params.desc_size; efi.memmap.desc_size = params.desc_size;
memmap.desc_version = params.desc_ver; 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) if (uefi_init() < 0)
return; return;
reserve_regions(); 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)) { memblock_reserve(params.mmap & PAGE_MASK,
/* PAGE_ALIGN(params.mmap_size +
* ARM currently does not allow ioremap_cache() to be called on (params.mmap & ~PAGE_MASK)));
* memory regions that are covered by struct page. So remove the
* UEFI memory map from the linear mapping. init_screen_info();
*/
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)));
}
} }
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);

View File

@ -42,11 +42,13 @@ static struct mm_struct efi_mm = {
static bool __init efi_virtmap_init(void) static bool __init efi_virtmap_init(void)
{ {
efi_memory_desc_t *md; efi_memory_desc_t *md;
bool systab_found;
efi_mm.pgd = pgd_alloc(&efi_mm); efi_mm.pgd = pgd_alloc(&efi_mm);
init_new_context(NULL, &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; phys_addr_t phys = md->phys_addr;
int ret; int ret;
@ -64,7 +66,25 @@ static bool __init efi_virtmap_init(void)
&phys, ret); &phys, ret);
return false; return false;
} }
/*
* If this entry covers the address of the UEFI system table,
* calculate and record its virtual address.
*/
if (efi_system_table >= phys &&
efi_system_table < phys + (md->num_pages * EFI_PAGE_SIZE)) {
efi.systab = (void *)(unsigned long)(efi_system_table -
phys + md->virt_addr);
systab_found = true;
}
} }
if (!systab_found) {
pr_err("No virtual mapping found for the UEFI System Table\n");
return false;
}
if (efi_memattr_apply_permissions(&efi_mm, efi_set_mapping_permissions))
return false;
return true; return true;
} }
@ -89,26 +109,17 @@ static int __init arm_enable_runtime_services(void)
pr_info("Remapping and enabling EFI services.\n"); pr_info("Remapping and enabling EFI services.\n");
mapsize = memmap.map_end - memmap.map; mapsize = efi.memmap.map_end - efi.memmap.map;
memmap.map = (__force void *)ioremap_cache(memmap.phys_map,
mapsize); efi.memmap.map = memremap(efi.memmap.phys_map, mapsize, MEMREMAP_WB);
if (!memmap.map) { if (!efi.memmap.map) {
pr_err("Failed to remap EFI memory map\n"); pr_err("Failed to remap EFI memory map\n");
return -ENOMEM; return -ENOMEM;
} }
memmap.map_end = memmap.map + mapsize; efi.memmap.map_end = efi.memmap.map + mapsize;
efi.memmap = &memmap;
efi.systab = (__force void *)ioremap_cache(efi_system_table,
sizeof(efi_system_table_t));
if (!efi.systab) {
pr_err("Failed to remap EFI System Table\n");
return -ENOMEM;
}
set_bit(EFI_SYSTEM_TABLES, &efi.flags);
if (!efi_virtmap_init()) { if (!efi_virtmap_init()) {
pr_err("No UEFI virtual mapping was installed -- runtime services will not be available\n"); pr_err("UEFI virtual mapping missing or invalid -- runtime services will not be available\n");
return -ENOMEM; return -ENOMEM;
} }
@ -116,8 +127,6 @@ static int __init arm_enable_runtime_services(void)
efi_native_runtime_setup(); efi_native_runtime_setup();
set_bit(EFI_RUNTIME_SERVICES, &efi.flags); set_bit(EFI_RUNTIME_SERVICES, &efi.flags);
efi.runtime_version = efi.systab->hdr.revision;
return 0; return 0;
} }
early_initcall(arm_enable_runtime_services); early_initcall(arm_enable_runtime_services);

View File

@ -0,0 +1,343 @@
/*
* EFI capsule loader driver.
*
* Copyright 2015 Intel Corporation
*
* This file is part of the Linux kernel, and is made available under
* the terms of the GNU General Public License version 2.
*/
#define pr_fmt(fmt) "efi: " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/highmem.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/efi.h>
#define NO_FURTHER_WRITE_ACTION -1
struct capsule_info {
bool header_obtained;
int reset_type;
long index;
size_t count;
size_t total_size;
struct page **pages;
size_t page_bytes_remain;
};
/**
* efi_free_all_buff_pages - free all previous allocated buffer pages
* @cap_info: pointer to current instance of capsule_info structure
*
* In addition to freeing buffer pages, it flags NO_FURTHER_WRITE_ACTION
* to cease processing data in subsequent write(2) calls until close(2)
* is called.
**/
static void efi_free_all_buff_pages(struct capsule_info *cap_info)
{
while (cap_info->index > 0)
__free_page(cap_info->pages[--cap_info->index]);
cap_info->index = NO_FURTHER_WRITE_ACTION;
}
/**
* efi_capsule_setup_info - obtain the efi capsule header in the binary and
* setup capsule_info structure
* @cap_info: pointer to current instance of capsule_info structure
* @kbuff: a mapped first page buffer pointer
* @hdr_bytes: the total received number of bytes for efi header
**/
static ssize_t efi_capsule_setup_info(struct capsule_info *cap_info,
void *kbuff, size_t hdr_bytes)
{
efi_capsule_header_t *cap_hdr;
size_t pages_needed;
int ret;
void *temp_page;
/* Only process data block that is larger than efi header size */
if (hdr_bytes < sizeof(efi_capsule_header_t))
return 0;
/* Reset back to the correct offset of header */
cap_hdr = kbuff - cap_info->count;
pages_needed = ALIGN(cap_hdr->imagesize, PAGE_SIZE) >> PAGE_SHIFT;
if (pages_needed == 0) {
pr_err("%s: pages count invalid\n", __func__);
return -EINVAL;
}
/* Check if the capsule binary supported */
ret = efi_capsule_supported(cap_hdr->guid, cap_hdr->flags,
cap_hdr->imagesize,
&cap_info->reset_type);
if (ret) {
pr_err("%s: efi_capsule_supported() failed\n",
__func__);
return ret;
}
cap_info->total_size = cap_hdr->imagesize;
temp_page = krealloc(cap_info->pages,
pages_needed * sizeof(void *),
GFP_KERNEL | __GFP_ZERO);
if (!temp_page) {
pr_debug("%s: krealloc() failed\n", __func__);
return -ENOMEM;
}
cap_info->pages = temp_page;
cap_info->header_obtained = true;
return 0;
}
/**
* efi_capsule_submit_update - invoke the efi_capsule_update API once binary
* upload done
* @cap_info: pointer to current instance of capsule_info structure
**/
static ssize_t efi_capsule_submit_update(struct capsule_info *cap_info)
{
int ret;
void *cap_hdr_temp;
cap_hdr_temp = kmap(cap_info->pages[0]);
if (!cap_hdr_temp) {
pr_debug("%s: kmap() failed\n", __func__);
return -EFAULT;
}
ret = efi_capsule_update(cap_hdr_temp, cap_info->pages);
kunmap(cap_info->pages[0]);
if (ret) {
pr_err("%s: efi_capsule_update() failed\n", __func__);
return ret;
}
/* Indicate capsule binary uploading is done */
cap_info->index = NO_FURTHER_WRITE_ACTION;
pr_info("%s: Successfully upload capsule file with reboot type '%s'\n",
__func__, !cap_info->reset_type ? "RESET_COLD" :
cap_info->reset_type == 1 ? "RESET_WARM" :
"RESET_SHUTDOWN");
return 0;
}
/**
* efi_capsule_write - store the capsule binary and pass it to
* efi_capsule_update() API
* @file: file pointer
* @buff: buffer pointer
* @count: number of bytes in @buff
* @offp: not used
*
* Expectation:
* - A user space tool should start at the beginning of capsule binary and
* pass data in sequentially.
* - Users should close and re-open this file note in order to upload more
* capsules.
* - After an error returned, user should close the file and restart the
* operation for the next try otherwise -EIO will be returned until the
* file is closed.
* - An EFI capsule header must be located at the beginning of capsule
* binary file and passed in as first block data of write operation.
**/
static ssize_t efi_capsule_write(struct file *file, const char __user *buff,
size_t count, loff_t *offp)
{
int ret = 0;
struct capsule_info *cap_info = file->private_data;
struct page *page;
void *kbuff = NULL;
size_t write_byte;
if (count == 0)
return 0;
/* Return error while NO_FURTHER_WRITE_ACTION is flagged */
if (cap_info->index < 0)
return -EIO;
/* Only alloc a new page when previous page is full */
if (!cap_info->page_bytes_remain) {
page = alloc_page(GFP_KERNEL);
if (!page) {
pr_debug("%s: alloc_page() failed\n", __func__);
ret = -ENOMEM;
goto failed;
}
cap_info->pages[cap_info->index++] = page;
cap_info->page_bytes_remain = PAGE_SIZE;
}
page = cap_info->pages[cap_info->index - 1];
kbuff = kmap(page);
if (!kbuff) {
pr_debug("%s: kmap() failed\n", __func__);
ret = -EFAULT;
goto failed;
}
kbuff += PAGE_SIZE - cap_info->page_bytes_remain;
/* Copy capsule binary data from user space to kernel space buffer */
write_byte = min_t(size_t, count, cap_info->page_bytes_remain);
if (copy_from_user(kbuff, buff, write_byte)) {
pr_debug("%s: copy_from_user() failed\n", __func__);
ret = -EFAULT;
goto fail_unmap;
}
cap_info->page_bytes_remain -= write_byte;
/* Setup capsule binary info structure */
if (!cap_info->header_obtained) {
ret = efi_capsule_setup_info(cap_info, kbuff,
cap_info->count + write_byte);
if (ret)
goto fail_unmap;
}
cap_info->count += write_byte;
kunmap(page);
/* Submit the full binary to efi_capsule_update() API */
if (cap_info->header_obtained &&
cap_info->count >= cap_info->total_size) {
if (cap_info->count > cap_info->total_size) {
pr_err("%s: upload size exceeded header defined size\n",
__func__);
ret = -EINVAL;
goto failed;
}
ret = efi_capsule_submit_update(cap_info);
if (ret)
goto failed;
}
return write_byte;
fail_unmap:
kunmap(page);
failed:
efi_free_all_buff_pages(cap_info);
return ret;
}
/**
* efi_capsule_flush - called by file close or file flush
* @file: file pointer
* @id: not used
*
* If a capsule is being partially uploaded then calling this function
* will be treated as upload termination and will free those completed
* buffer pages and -ECANCELED will be returned.
**/
static int efi_capsule_flush(struct file *file, fl_owner_t id)
{
int ret = 0;
struct capsule_info *cap_info = file->private_data;
if (cap_info->index > 0) {
pr_err("%s: capsule upload not complete\n", __func__);
efi_free_all_buff_pages(cap_info);
ret = -ECANCELED;
}
return ret;
}
/**
* efi_capsule_release - called by file close
* @inode: not used
* @file: file pointer
*
* We will not free successfully submitted pages since efi update
* requires data to be maintained across system reboot.
**/
static int efi_capsule_release(struct inode *inode, struct file *file)
{
struct capsule_info *cap_info = file->private_data;
kfree(cap_info->pages);
kfree(file->private_data);
file->private_data = NULL;
return 0;
}
/**
* efi_capsule_open - called by file open
* @inode: not used
* @file: file pointer
*
* Will allocate each capsule_info memory for each file open call.
* This provided the capability to support multiple file open feature
* where user is not needed to wait for others to finish in order to
* upload their capsule binary.
**/
static int efi_capsule_open(struct inode *inode, struct file *file)
{
struct capsule_info *cap_info;
cap_info = kzalloc(sizeof(*cap_info), GFP_KERNEL);
if (!cap_info)
return -ENOMEM;
cap_info->pages = kzalloc(sizeof(void *), GFP_KERNEL);
if (!cap_info->pages) {
kfree(cap_info);
return -ENOMEM;
}
file->private_data = cap_info;
return 0;
}
static const struct file_operations efi_capsule_fops = {
.owner = THIS_MODULE,
.open = efi_capsule_open,
.write = efi_capsule_write,
.flush = efi_capsule_flush,
.release = efi_capsule_release,
.llseek = no_llseek,
};
static struct miscdevice efi_capsule_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "efi_capsule_loader",
.fops = &efi_capsule_fops,
};
static int __init efi_capsule_loader_init(void)
{
int ret;
if (!efi_enabled(EFI_RUNTIME_SERVICES))
return -ENODEV;
ret = misc_register(&efi_capsule_misc);
if (ret)
pr_err("%s: Failed to register misc char file note\n",
__func__);
return ret;
}
module_init(efi_capsule_loader_init);
static void __exit efi_capsule_loader_exit(void)
{
misc_deregister(&efi_capsule_misc);
}
module_exit(efi_capsule_loader_exit);
MODULE_DESCRIPTION("EFI capsule firmware binary loader");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,308 @@
/*
* EFI capsule support.
*
* Copyright 2013 Intel Corporation; author Matt Fleming
*
* This file is part of the Linux kernel, and is made available under
* the terms of the GNU General Public License version 2.
*/
#define pr_fmt(fmt) "efi: " fmt
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/highmem.h>
#include <linux/efi.h>
#include <linux/vmalloc.h>
#include <asm/io.h>
typedef struct {
u64 length;
u64 data;
} efi_capsule_block_desc_t;
static bool capsule_pending;
static bool stop_capsules;
static int efi_reset_type = -1;
/*
* capsule_mutex serialises access to both capsule_pending and
* efi_reset_type and stop_capsules.
*/
static DEFINE_MUTEX(capsule_mutex);
/**
* efi_capsule_pending - has a capsule been passed to the firmware?
* @reset_type: store the type of EFI reset if capsule is pending
*
* To ensure that the registered capsule is processed correctly by the
* firmware we need to perform a specific type of reset. If a capsule is
* pending return the reset type in @reset_type.
*
* This function will race with callers of efi_capsule_update(), for
* example, calling this function while somebody else is in
* efi_capsule_update() but hasn't reached efi_capsue_update_locked()
* will miss the updates to capsule_pending and efi_reset_type after
* efi_capsule_update_locked() completes.
*
* A non-racy use is from platform reboot code because we use
* system_state to ensure no capsules can be sent to the firmware once
* we're at SYSTEM_RESTART. See efi_capsule_update_locked().
*/
bool efi_capsule_pending(int *reset_type)
{
if (!capsule_pending)
return false;
if (reset_type)
*reset_type = efi_reset_type;
return true;
}
/*
* Whitelist of EFI capsule flags that we support.
*
* We do not handle EFI_CAPSULE_INITIATE_RESET because that would
* require us to prepare the kernel for reboot. Refuse to load any
* capsules with that flag and any other flags that we do not know how
* to handle.
*/
#define EFI_CAPSULE_SUPPORTED_FLAG_MASK \
(EFI_CAPSULE_PERSIST_ACROSS_RESET | EFI_CAPSULE_POPULATE_SYSTEM_TABLE)
/**
* efi_capsule_supported - does the firmware support the capsule?
* @guid: vendor guid of capsule
* @flags: capsule flags
* @size: size of capsule data
* @reset: the reset type required for this capsule
*
* Check whether a capsule with @flags is supported by the firmware
* and that @size doesn't exceed the maximum size for a capsule.
*
* No attempt is made to check @reset against the reset type required
* by any pending capsules because of the races involved.
*/
int efi_capsule_supported(efi_guid_t guid, u32 flags, size_t size, int *reset)
{
efi_capsule_header_t capsule;
efi_capsule_header_t *cap_list[] = { &capsule };
efi_status_t status;
u64 max_size;
if (flags & ~EFI_CAPSULE_SUPPORTED_FLAG_MASK)
return -EINVAL;
capsule.headersize = capsule.imagesize = sizeof(capsule);
memcpy(&capsule.guid, &guid, sizeof(efi_guid_t));
capsule.flags = flags;
status = efi.query_capsule_caps(cap_list, 1, &max_size, reset);
if (status != EFI_SUCCESS)
return efi_status_to_err(status);
if (size > max_size)
return -ENOSPC;
return 0;
}
EXPORT_SYMBOL_GPL(efi_capsule_supported);
/*
* Every scatter gather list (block descriptor) page must end with a
* continuation pointer. The last continuation pointer of the last
* page must be zero to mark the end of the chain.
*/
#define SGLIST_PER_PAGE ((PAGE_SIZE / sizeof(efi_capsule_block_desc_t)) - 1)
/*
* How many scatter gather list (block descriptor) pages do we need
* to map @count pages?
*/
static inline unsigned int sg_pages_num(unsigned int count)
{
return DIV_ROUND_UP(count, SGLIST_PER_PAGE);
}
/**
* efi_capsule_update_locked - pass a single capsule to the firmware
* @capsule: capsule to send to the firmware
* @sg_pages: array of scatter gather (block descriptor) pages
* @reset: the reset type required for @capsule
*
* Since this function must be called under capsule_mutex check
* whether efi_reset_type will conflict with @reset, and atomically
* set it and capsule_pending if a capsule was successfully sent to
* the firmware.
*
* We also check to see if the system is about to restart, and if so,
* abort. This avoids races between efi_capsule_update() and
* efi_capsule_pending().
*/
static int
efi_capsule_update_locked(efi_capsule_header_t *capsule,
struct page **sg_pages, int reset)
{
efi_physical_addr_t sglist_phys;
efi_status_t status;
lockdep_assert_held(&capsule_mutex);
/*
* If someone has already registered a capsule that requires a
* different reset type, we're out of luck and must abort.
*/
if (efi_reset_type >= 0 && efi_reset_type != reset) {
pr_err("Conflicting capsule reset type %d (%d).\n",
reset, efi_reset_type);
return -EINVAL;
}
/*
* If the system is getting ready to restart it may have
* called efi_capsule_pending() to make decisions (such as
* whether to force an EFI reboot), and we're racing against
* that call. Abort in that case.
*/
if (unlikely(stop_capsules)) {
pr_warn("Capsule update raced with reboot, aborting.\n");
return -EINVAL;
}
sglist_phys = page_to_phys(sg_pages[0]);
status = efi.update_capsule(&capsule, 1, sglist_phys);
if (status == EFI_SUCCESS) {
capsule_pending = true;
efi_reset_type = reset;
}
return efi_status_to_err(status);
}
/**
* efi_capsule_update - send a capsule to the firmware
* @capsule: capsule to send to firmware
* @pages: an array of capsule data pages
*
* Build a scatter gather list with EFI capsule block descriptors to
* map the capsule described by @capsule with its data in @pages and
* send it to the firmware via the UpdateCapsule() runtime service.
*
* @capsule must be a virtual mapping of the first page in @pages
* (@pages[0]) in the kernel address space. That is, a
* capsule_header_t that describes the entire contents of the capsule
* must be at the start of the first data page.
*
* Even though this function will validate that the firmware supports
* the capsule guid, users will likely want to check that
* efi_capsule_supported() returns true before calling this function
* because it makes it easier to print helpful error messages.
*
* If the capsule is successfully submitted to the firmware, any
* subsequent calls to efi_capsule_pending() will return true. @pages
* must not be released or modified if this function returns
* successfully.
*
* Callers must be prepared for this function to fail, which can
* happen if we raced with system reboot or if there is already a
* pending capsule that has a reset type that conflicts with the one
* required by @capsule. Do NOT use efi_capsule_pending() to detect
* this conflict since that would be racy. Instead, submit the capsule
* to efi_capsule_update() and check the return value.
*
* Return 0 on success, a converted EFI status code on failure.
*/
int efi_capsule_update(efi_capsule_header_t *capsule, struct page **pages)
{
u32 imagesize = capsule->imagesize;
efi_guid_t guid = capsule->guid;
unsigned int count, sg_count;
u32 flags = capsule->flags;
struct page **sg_pages;
int rv, reset_type;
int i, j;
rv = efi_capsule_supported(guid, flags, imagesize, &reset_type);
if (rv)
return rv;
count = DIV_ROUND_UP(imagesize, PAGE_SIZE);
sg_count = sg_pages_num(count);
sg_pages = kzalloc(sg_count * sizeof(*sg_pages), GFP_KERNEL);
if (!sg_pages)
return -ENOMEM;
for (i = 0; i < sg_count; i++) {
sg_pages[i] = alloc_page(GFP_KERNEL);
if (!sg_pages[i]) {
rv = -ENOMEM;
goto out;
}
}
for (i = 0; i < sg_count; i++) {
efi_capsule_block_desc_t *sglist;
sglist = kmap(sg_pages[i]);
if (!sglist) {
rv = -ENOMEM;
goto out;
}
for (j = 0; j < SGLIST_PER_PAGE && count > 0; j++) {
u64 sz = min_t(u64, imagesize, PAGE_SIZE);
sglist[j].length = sz;
sglist[j].data = page_to_phys(*pages++);
imagesize -= sz;
count--;
}
/* Continuation pointer */
sglist[j].length = 0;
if (i + 1 == sg_count)
sglist[j].data = 0;
else
sglist[j].data = page_to_phys(sg_pages[i + 1]);
kunmap(sg_pages[i]);
}
mutex_lock(&capsule_mutex);
rv = efi_capsule_update_locked(capsule, sg_pages, reset_type);
mutex_unlock(&capsule_mutex);
out:
for (i = 0; rv && i < sg_count; i++) {
if (sg_pages[i])
__free_page(sg_pages[i]);
}
kfree(sg_pages);
return rv;
}
EXPORT_SYMBOL_GPL(efi_capsule_update);
static int capsule_reboot_notify(struct notifier_block *nb, unsigned long event, void *cmd)
{
mutex_lock(&capsule_mutex);
stop_capsules = true;
mutex_unlock(&capsule_mutex);
return NOTIFY_DONE;
}
static struct notifier_block capsule_reboot_nb = {
.notifier_call = capsule_reboot_notify,
};
static int __init capsule_reboot_register(void)
{
return register_reboot_notifier(&capsule_reboot_nb);
}
core_initcall(capsule_reboot_register);

View File

@ -43,6 +43,7 @@ struct efi __read_mostly efi = {
.config_table = EFI_INVALID_TABLE_ADDR, .config_table = EFI_INVALID_TABLE_ADDR,
.esrt = EFI_INVALID_TABLE_ADDR, .esrt = EFI_INVALID_TABLE_ADDR,
.properties_table = EFI_INVALID_TABLE_ADDR, .properties_table = EFI_INVALID_TABLE_ADDR,
.mem_attr_table = EFI_INVALID_TABLE_ADDR,
}; };
EXPORT_SYMBOL(efi); 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) 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; phys_addr_t p, e;
if (!efi_enabled(EFI_MEMMAP)) { 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}, {UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga},
{EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt}, {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt},
{EFI_PROPERTIES_TABLE_GUID, "PROP", &efi.properties_table}, {EFI_PROPERTIES_TABLE_GUID, "PROP", &efi.properties_table},
{EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi.mem_attr_table},
{NULL_GUID, NULL, NULL}, {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++) { for (i = 0; efi_guidcmp(table_types[i].guid, NULL_GUID); i++) {
if (!efi_guidcmp(*guid, table_types[i].guid)) { if (!efi_guidcmp(*guid, table_types[i].guid)) {
*(table_types[i].ptr) = table; *(table_types[i].ptr) = table;
pr_cont(" %s=0x%lx ", if (table_types[i].name)
table_types[i].name, table); pr_cont(" %s=0x%lx ",
table_types[i].name, table);
return 1; 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) u64 __weak efi_mem_attributes(unsigned long phys_addr)
{ {
struct efi_memory_map *map;
efi_memory_desc_t *md; efi_memory_desc_t *md;
void *p;
if (!efi_enabled(EFI_MEMMAP)) if (!efi_enabled(EFI_MEMMAP))
return 0; return 0;
map = efi.memmap; for_each_efi_memory_desc(md) {
for (p = map->map; p < map->map_end; p += map->desc_size) {
md = p;
if ((md->phys_addr <= phys_addr) && if ((md->phys_addr <= phys_addr) &&
(phys_addr < (md->phys_addr + (phys_addr < (md->phys_addr +
(md->num_pages << EFI_PAGE_SHIFT)))) (md->num_pages << EFI_PAGE_SHIFT))))
@ -637,3 +636,36 @@ u64 __weak efi_mem_attributes(unsigned long phys_addr)
} }
return 0; 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;
}

View File

@ -0,0 +1,113 @@
/*
* efibc: control EFI bootloaders which obey LoaderEntryOneShot var
* Copyright (c) 2013-2016, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#define pr_fmt(fmt) "efibc: " fmt
#include <linux/efi.h>
#include <linux/module.h>
#include <linux/reboot.h>
#include <linux/slab.h>
static void efibc_str_to_str16(const char *str, efi_char16_t *str16)
{
size_t i;
for (i = 0; i < strlen(str); i++)
str16[i] = str[i];
str16[i] = '\0';
}
static int efibc_set_variable(const char *name, const char *value)
{
int ret;
efi_guid_t guid = LINUX_EFI_LOADER_ENTRY_GUID;
struct efivar_entry *entry;
size_t size = (strlen(value) + 1) * sizeof(efi_char16_t);
if (size > sizeof(entry->var.Data)) {
pr_err("value is too large");
return -EINVAL;
}
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry) {
pr_err("failed to allocate efivar entry");
return -ENOMEM;
}
efibc_str_to_str16(name, entry->var.VariableName);
efibc_str_to_str16(value, (efi_char16_t *)entry->var.Data);
memcpy(&entry->var.VendorGuid, &guid, sizeof(guid));
ret = efivar_entry_set(entry,
EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS,
size, entry->var.Data, NULL);
if (ret)
pr_err("failed to set %s EFI variable: 0x%x\n",
name, ret);
kfree(entry);
return ret;
}
static int efibc_reboot_notifier_call(struct notifier_block *notifier,
unsigned long event, void *data)
{
const char *reason = "shutdown";
int ret;
if (event == SYS_RESTART)
reason = "reboot";
ret = efibc_set_variable("LoaderEntryRebootReason", reason);
if (ret || !data)
return NOTIFY_DONE;
efibc_set_variable("LoaderEntryOneShot", (char *)data);
return NOTIFY_DONE;
}
static struct notifier_block efibc_reboot_notifier = {
.notifier_call = efibc_reboot_notifier_call,
};
static int __init efibc_init(void)
{
int ret;
if (!efi_enabled(EFI_RUNTIME_SERVICES))
return -ENODEV;
ret = register_reboot_notifier(&efibc_reboot_notifier);
if (ret)
pr_err("unable to register reboot notifier\n");
return ret;
}
module_init(efibc_init);
static void __exit efibc_exit(void)
{
unregister_reboot_notifier(&efibc_reboot_notifier);
}
module_exit(efibc_exit);
MODULE_AUTHOR("Jeremy Compostella <jeremy.compostella@intel.com>");
MODULE_AUTHOR("Matt Gumbel <matthew.k.gumbel@intel.com");
MODULE_DESCRIPTION("EFI Bootloader Control");
MODULE_LICENSE("GPL v2");

View File

@ -661,7 +661,7 @@ static void efivar_update_sysfs_entries(struct work_struct *work)
return; return;
err = efivar_init(efivar_update_sysfs_entry, entry, err = efivar_init(efivar_update_sysfs_entry, entry,
true, false, &efivar_sysfs_list); false, &efivar_sysfs_list);
if (!err) if (!err)
break; break;
@ -730,8 +730,7 @@ int efivars_sysfs_init(void)
return -ENOMEM; return -ENOMEM;
} }
efivar_init(efivars_sysfs_callback, NULL, false, efivar_init(efivars_sysfs_callback, NULL, true, &efivar_sysfs_list);
true, &efivar_sysfs_list);
error = create_efivars_bin_attributes(); error = create_efivars_bin_attributes();
if (error) { if (error) {

View File

@ -57,7 +57,7 @@ static int __init cmp_fake_mem(const void *x1, const void *x2)
void __init efi_fake_memmap(void) void __init efi_fake_memmap(void)
{ {
u64 start, end, m_start, m_end, m_attr; 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; efi_memory_desc_t *md;
phys_addr_t new_memmap_phy; phys_addr_t new_memmap_phy;
void *new_memmap; void *new_memmap;
@ -68,8 +68,7 @@ void __init efi_fake_memmap(void)
return; return;
/* count up the number of EFI memory descriptor */ /* count up the number of EFI memory descriptor */
for (old = memmap.map; old < memmap.map_end; old += memmap.desc_size) { for_each_efi_memory_desc(md) {
md = old;
start = md->phys_addr; start = md->phys_addr;
end = start + (md->num_pages << EFI_PAGE_SHIFT) - 1; 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 */ /* 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); PAGE_SIZE);
if (!new_memmap_phy) if (!new_memmap_phy)
return; return;
/* create new EFI memmap */ /* create new EFI memmap */
new_memmap = early_memremap(new_memmap_phy, new_memmap = early_memremap(new_memmap_phy,
memmap.desc_size * new_nr_map); efi.memmap.desc_size * new_nr_map);
if (!new_memmap) { 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; return;
} }
for (old = memmap.map, new = new_memmap; for (old = efi.memmap.map, new = new_memmap;
old < memmap.map_end; old < efi.memmap.map_end;
old += memmap.desc_size, new += memmap.desc_size) { old += efi.memmap.desc_size, new += efi.memmap.desc_size) {
/* copy original EFI memory descriptor */ /* copy original EFI memory descriptor */
memcpy(new, old, memmap.desc_size); memcpy(new, old, efi.memmap.desc_size);
md = new; md = new;
start = md->phys_addr; start = md->phys_addr;
end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1; 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) >> md->num_pages = (m_end - md->phys_addr + 1) >>
EFI_PAGE_SHIFT; EFI_PAGE_SHIFT;
/* latter part */ /* latter part */
new += memmap.desc_size; new += efi.memmap.desc_size;
memcpy(new, old, memmap.desc_size); memcpy(new, old, efi.memmap.desc_size);
md = new; md = new;
md->phys_addr = m_end + 1; md->phys_addr = m_end + 1;
md->num_pages = (end - md->phys_addr + 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) >> md->num_pages = (m_start - md->phys_addr) >>
EFI_PAGE_SHIFT; EFI_PAGE_SHIFT;
/* middle part */ /* middle part */
new += memmap.desc_size; new += efi.memmap.desc_size;
memcpy(new, old, memmap.desc_size); memcpy(new, old, efi.memmap.desc_size);
md = new; md = new;
md->attribute |= m_attr; md->attribute |= m_attr;
md->phys_addr = m_start; md->phys_addr = m_start;
md->num_pages = (m_end - m_start + 1) >> md->num_pages = (m_end - m_start + 1) >>
EFI_PAGE_SHIFT; EFI_PAGE_SHIFT;
/* last part */ /* last part */
new += memmap.desc_size; new += efi.memmap.desc_size;
memcpy(new, old, memmap.desc_size); memcpy(new, old, efi.memmap.desc_size);
md = new; md = new;
md->phys_addr = m_end + 1; md->phys_addr = m_end + 1;
md->num_pages = (end - m_end) >> md->num_pages = (end - m_end) >>
@ -169,8 +168,8 @@ void __init efi_fake_memmap(void)
md->num_pages = (m_start - md->phys_addr) >> md->num_pages = (m_start - md->phys_addr) >>
EFI_PAGE_SHIFT; EFI_PAGE_SHIFT;
/* latter part */ /* latter part */
new += memmap.desc_size; new += efi.memmap.desc_size;
memcpy(new, old, memmap.desc_size); memcpy(new, old, efi.memmap.desc_size);
md = new; md = new;
md->phys_addr = m_start; md->phys_addr = m_start;
md->num_pages = (end - md->phys_addr + 1) >> md->num_pages = (end - md->phys_addr + 1) >>
@ -182,10 +181,10 @@ void __init efi_fake_memmap(void)
/* swap into new EFI memmap */ /* swap into new EFI memmap */
efi_unmap_memmap(); efi_unmap_memmap();
memmap.map = new_memmap; efi.memmap.map = new_memmap;
memmap.phys_map = new_memmap_phy; efi.memmap.phys_map = new_memmap_phy;
memmap.nr_map = new_nr_map; efi.memmap.nr_map = new_nr_map;
memmap.map_end = memmap.map + memmap.nr_map * memmap.desc_size; efi.memmap.map_end = efi.memmap.map + efi.memmap.nr_map * efi.memmap.desc_size;
set_bit(EFI_MEMMAP, &efi.flags); set_bit(EFI_MEMMAP, &efi.flags);
/* print new EFI memmap */ /* print new EFI memmap */

View File

@ -28,7 +28,7 @@ OBJECT_FILES_NON_STANDARD := y
# Prevents link failures: __sanitizer_cov_trace_pc() is not linked in. # Prevents link failures: __sanitizer_cov_trace_pc() is not linked in.
KCOV_INSTRUMENT := n 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 # 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 arm-deps := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c sort.c

View File

@ -20,27 +20,49 @@
bool __nokaslr; 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 sb_var_name[] = {
static efi_char16_t const var_name[] = {
'S', 'e', 'c', 'u', 'r', 'e', 'B', 'o', 'o', 't', 0 }; '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; efi_get_variable_t *f_getvar = sys_table_arg->runtime->get_variable;
unsigned long size = sizeof(u8);
efi_status_t status;
u8 val; 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); 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) { switch (status) {
case EFI_SUCCESS:
return val;
case EFI_NOT_FOUND: case EFI_NOT_FOUND:
return 0; return 0;
case EFI_DEVICE_ERROR:
return -EIO;
case EFI_SECURITY_VIOLATION:
return -EACCES;
default: 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); 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 * 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; efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID;
unsigned long reserve_addr = 0; unsigned long reserve_addr = 0;
unsigned long reserve_size = 0; unsigned long reserve_size = 0;
int secure_boot = 0;
struct screen_info *si;
/* Check if we were booted by the EFI firmware */ /* Check if we were booted by the EFI firmware */
if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) 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; __nokaslr = true;
} }
si = setup_graphics(sys_table);
status = handle_kernel_image(sys_table, image_addr, &image_size, status = handle_kernel_image(sys_table, image_addr, &image_size,
&reserve_addr, &reserve_addr,
&reserve_size, &reserve_size,
@ -250,12 +295,21 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
pr_efi_err(sys_table, "Failed to parse EFI cmdline options\n"); pr_efi_err(sys_table, "Failed to parse EFI cmdline options\n");
secure_boot = efi_get_secureboot(sys_table);
if (secure_boot > 0)
pr_efi(sys_table, "UEFI Secure Boot is enabled.\n");
if (secure_boot < 0) {
pr_efi_err(sys_table,
"could not determine UEFI Secure Boot status.\n");
}
/* /*
* Unauthenticated device tree data is a security hazard, so * Unauthenticated device tree data is a security hazard, so
* ignore 'dtb=' unless UEFI Secure Boot is disabled. * ignore 'dtb=' unless UEFI Secure Boot is disabled.
*/ */
if (efi_secureboot_enabled(sys_table)) { if (secure_boot != 0 && strstr(cmdline_ptr, "dtb=")) {
pr_efi(sys_table, "UEFI Secure Boot is enabled.\n"); pr_efi(sys_table, "Ignoring DTB from command line.\n");
} else { } else {
status = handle_cmdline_files(sys_table, image, cmdline_ptr, status = handle_cmdline_files(sys_table, image, cmdline_ptr,
"dtb=", "dtb=",
@ -309,6 +363,7 @@ fail_free_image:
efi_free(sys_table, image_size, *image_addr); efi_free(sys_table, image_size, *image_addr);
efi_free(sys_table, reserve_size, reserve_addr); efi_free(sys_table, reserve_size, reserve_addr);
fail_free_cmdline: fail_free_cmdline:
free_screen_info(sys_table, si);
efi_free(sys_table, cmdline_size, (unsigned long)cmdline_ptr); efi_free(sys_table, cmdline_size, (unsigned long)cmdline_ptr);
fail: fail:
return EFI_ERROR; return EFI_ERROR;

View File

@ -26,6 +26,43 @@ efi_status_t check_platform_features(efi_system_table_t *sys_table_arg)
return EFI_SUCCESS; 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, efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
unsigned long *image_addr, unsigned long *image_addr,
unsigned long *image_size, unsigned long *image_size,

View File

@ -125,10 +125,12 @@ unsigned long get_dram_base(efi_system_table_t *sys_table_arg)
map.map_end = map.map + map_size; map.map_end = map.map + map_size;
for_each_efi_memory_desc(&map, md) for_each_efi_memory_desc_in_map(&map, md) {
if (md->attribute & EFI_MEMORY_WB) if (md->attribute & EFI_MEMORY_WB) {
if (membase > md->phys_addr) if (membase > md->phys_addr)
membase = md->phys_addr; membase = md->phys_addr;
}
}
efi_call_early(free_pool, map.map); efi_call_early(free_pool, map.map);

View File

@ -0,0 +1,354 @@
/* -----------------------------------------------------------------------
*
* Copyright 2011 Intel Corporation; author Matt Fleming
*
* This file is part of the Linux kernel, and is made available under
* the terms of the GNU General Public License version 2.
*
* ----------------------------------------------------------------------- */
#include <linux/efi.h>
#include <linux/screen_info.h>
#include <asm/efi.h>
#include <asm/setup.h>
static void find_bits(unsigned long mask, u8 *pos, u8 *size)
{
u8 first, len;
first = 0;
len = 0;
if (mask) {
while (!(mask & 0x1)) {
mask = mask >> 1;
first++;
}
while (mask & 0x1) {
mask = mask >> 1;
len++;
}
}
*pos = first;
*size = len;
}
static void
setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
struct efi_pixel_bitmask pixel_info, int pixel_format)
{
if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
si->lfb_depth = 32;
si->lfb_linelength = pixels_per_scan_line * 4;
si->red_size = 8;
si->red_pos = 0;
si->green_size = 8;
si->green_pos = 8;
si->blue_size = 8;
si->blue_pos = 16;
si->rsvd_size = 8;
si->rsvd_pos = 24;
} else if (pixel_format == PIXEL_BGR_RESERVED_8BIT_PER_COLOR) {
si->lfb_depth = 32;
si->lfb_linelength = pixels_per_scan_line * 4;
si->red_size = 8;
si->red_pos = 16;
si->green_size = 8;
si->green_pos = 8;
si->blue_size = 8;
si->blue_pos = 0;
si->rsvd_size = 8;
si->rsvd_pos = 24;
} else if (pixel_format == PIXEL_BIT_MASK) {
find_bits(pixel_info.red_mask, &si->red_pos, &si->red_size);
find_bits(pixel_info.green_mask, &si->green_pos,
&si->green_size);
find_bits(pixel_info.blue_mask, &si->blue_pos, &si->blue_size);
find_bits(pixel_info.reserved_mask, &si->rsvd_pos,
&si->rsvd_size);
si->lfb_depth = si->red_size + si->green_size +
si->blue_size + si->rsvd_size;
si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
} else {
si->lfb_depth = 4;
si->lfb_linelength = si->lfb_width / 2;
si->red_size = 0;
si->red_pos = 0;
si->green_size = 0;
si->green_pos = 0;
si->blue_size = 0;
si->blue_pos = 0;
si->rsvd_size = 0;
si->rsvd_pos = 0;
}
}
static efi_status_t
__gop_query32(efi_system_table_t *sys_table_arg,
struct efi_graphics_output_protocol_32 *gop32,
struct efi_graphics_output_mode_info **info,
unsigned long *size, u64 *fb_base)
{
struct efi_graphics_output_protocol_mode_32 *mode;
efi_graphics_output_protocol_query_mode query_mode;
efi_status_t status;
unsigned long m;
m = gop32->mode;
mode = (struct efi_graphics_output_protocol_mode_32 *)m;
query_mode = (void *)(unsigned long)gop32->query_mode;
status = __efi_call_early(query_mode, (void *)gop32, mode->mode, size,
info);
if (status != EFI_SUCCESS)
return status;
*fb_base = mode->frame_buffer_base;
return status;
}
static efi_status_t
setup_gop32(efi_system_table_t *sys_table_arg, struct screen_info *si,
efi_guid_t *proto, unsigned long size, void **gop_handle)
{
struct efi_graphics_output_protocol_32 *gop32, *first_gop;
unsigned long nr_gops;
u16 width, height;
u32 pixels_per_scan_line;
u32 ext_lfb_base;
u64 fb_base;
struct efi_pixel_bitmask pixel_info;
int pixel_format;
efi_status_t status = EFI_NOT_FOUND;
u32 *handles = (u32 *)(unsigned long)gop_handle;
int i;
first_gop = NULL;
gop32 = NULL;
nr_gops = size / sizeof(u32);
for (i = 0; i < nr_gops; i++) {
struct efi_graphics_output_mode_info *info = NULL;
efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
bool conout_found = false;
void *dummy = NULL;
efi_handle_t h = (efi_handle_t)(unsigned long)handles[i];
u64 current_fb_base;
status = efi_call_early(handle_protocol, h,
proto, (void **)&gop32);
if (status != EFI_SUCCESS)
continue;
status = efi_call_early(handle_protocol, h,
&conout_proto, &dummy);
if (status == EFI_SUCCESS)
conout_found = true;
status = __gop_query32(sys_table_arg, gop32, &info, &size,
&current_fb_base);
if (status == EFI_SUCCESS && (!first_gop || conout_found)) {
/*
* Systems that use the UEFI Console Splitter may
* provide multiple GOP devices, not all of which are
* backed by real hardware. The workaround is to search
* for a GOP implementing the ConOut protocol, and if
* one isn't found, to just fall back to the first GOP.
*/
width = info->horizontal_resolution;
height = info->vertical_resolution;
pixel_format = info->pixel_format;
pixel_info = info->pixel_information;
pixels_per_scan_line = info->pixels_per_scan_line;
fb_base = current_fb_base;
/*
* Once we've found a GOP supporting ConOut,
* don't bother looking any further.
*/
first_gop = gop32;
if (conout_found)
break;
}
}
/* Did we find any GOPs? */
if (!first_gop)
goto out;
/* EFI framebuffer */
si->orig_video_isVGA = VIDEO_TYPE_EFI;
si->lfb_width = width;
si->lfb_height = height;
si->lfb_base = fb_base;
ext_lfb_base = (u64)(unsigned long)fb_base >> 32;
if (ext_lfb_base) {
si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
si->ext_lfb_base = ext_lfb_base;
}
si->pages = 1;
setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format);
si->lfb_size = si->lfb_linelength * si->lfb_height;
si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
out:
return status;
}
static efi_status_t
__gop_query64(efi_system_table_t *sys_table_arg,
struct efi_graphics_output_protocol_64 *gop64,
struct efi_graphics_output_mode_info **info,
unsigned long *size, u64 *fb_base)
{
struct efi_graphics_output_protocol_mode_64 *mode;
efi_graphics_output_protocol_query_mode query_mode;
efi_status_t status;
unsigned long m;
m = gop64->mode;
mode = (struct efi_graphics_output_protocol_mode_64 *)m;
query_mode = (void *)(unsigned long)gop64->query_mode;
status = __efi_call_early(query_mode, (void *)gop64, mode->mode, size,
info);
if (status != EFI_SUCCESS)
return status;
*fb_base = mode->frame_buffer_base;
return status;
}
static efi_status_t
setup_gop64(efi_system_table_t *sys_table_arg, struct screen_info *si,
efi_guid_t *proto, unsigned long size, void **gop_handle)
{
struct efi_graphics_output_protocol_64 *gop64, *first_gop;
unsigned long nr_gops;
u16 width, height;
u32 pixels_per_scan_line;
u32 ext_lfb_base;
u64 fb_base;
struct efi_pixel_bitmask pixel_info;
int pixel_format;
efi_status_t status = EFI_NOT_FOUND;
u64 *handles = (u64 *)(unsigned long)gop_handle;
int i;
first_gop = NULL;
gop64 = NULL;
nr_gops = size / sizeof(u64);
for (i = 0; i < nr_gops; i++) {
struct efi_graphics_output_mode_info *info = NULL;
efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
bool conout_found = false;
void *dummy = NULL;
efi_handle_t h = (efi_handle_t)(unsigned long)handles[i];
u64 current_fb_base;
status = efi_call_early(handle_protocol, h,
proto, (void **)&gop64);
if (status != EFI_SUCCESS)
continue;
status = efi_call_early(handle_protocol, h,
&conout_proto, &dummy);
if (status == EFI_SUCCESS)
conout_found = true;
status = __gop_query64(sys_table_arg, gop64, &info, &size,
&current_fb_base);
if (status == EFI_SUCCESS && (!first_gop || conout_found)) {
/*
* Systems that use the UEFI Console Splitter may
* provide multiple GOP devices, not all of which are
* backed by real hardware. The workaround is to search
* for a GOP implementing the ConOut protocol, and if
* one isn't found, to just fall back to the first GOP.
*/
width = info->horizontal_resolution;
height = info->vertical_resolution;
pixel_format = info->pixel_format;
pixel_info = info->pixel_information;
pixels_per_scan_line = info->pixels_per_scan_line;
fb_base = current_fb_base;
/*
* Once we've found a GOP supporting ConOut,
* don't bother looking any further.
*/
first_gop = gop64;
if (conout_found)
break;
}
}
/* Did we find any GOPs? */
if (!first_gop)
goto out;
/* EFI framebuffer */
si->orig_video_isVGA = VIDEO_TYPE_EFI;
si->lfb_width = width;
si->lfb_height = height;
si->lfb_base = fb_base;
ext_lfb_base = (u64)(unsigned long)fb_base >> 32;
if (ext_lfb_base) {
si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
si->ext_lfb_base = ext_lfb_base;
}
si->pages = 1;
setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format);
si->lfb_size = si->lfb_linelength * si->lfb_height;
si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
out:
return status;
}
/*
* See if we have Graphics Output Protocol
*/
efi_status_t efi_setup_gop(efi_system_table_t *sys_table_arg,
struct screen_info *si, efi_guid_t *proto,
unsigned long size)
{
efi_status_t status;
void **gop_handle = NULL;
status = efi_call_early(allocate_pool, EFI_LOADER_DATA,
size, (void **)&gop_handle);
if (status != EFI_SUCCESS)
return status;
status = efi_call_early(locate_handle,
EFI_LOCATE_BY_PROTOCOL,
proto, NULL, &size, gop_handle);
if (status != EFI_SUCCESS)
goto free_handle;
if (efi_is_64bit()) {
status = setup_gop64(sys_table_arg, si, proto, size,
gop_handle);
} else {
status = setup_gop32(sys_table_arg, si, proto, size,
gop_handle);
}
free_handle:
efi_call_early(free_pool, gop_handle);
return status;
}

View File

@ -0,0 +1,182 @@
/*
* Copyright (C) 2016 Linaro Ltd. <ard.biesheuvel@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#define pr_fmt(fmt) "efi: memattr: " fmt
#include <linux/efi.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/memblock.h>
#include <asm/early_ioremap.h>
static int __initdata tbl_size;
/*
* Reserve the memory associated with the Memory Attributes configuration
* table, if it exists.
*/
int __init efi_memattr_init(void)
{
efi_memory_attributes_table_t *tbl;
if (efi.mem_attr_table == EFI_INVALID_TABLE_ADDR)
return 0;
tbl = early_memremap(efi.mem_attr_table, sizeof(*tbl));
if (!tbl) {
pr_err("Failed to map EFI Memory Attributes table @ 0x%lx\n",
efi.mem_attr_table);
return -ENOMEM;
}
if (tbl->version > 1) {
pr_warn("Unexpected EFI Memory Attributes table version %d\n",
tbl->version);
goto unmap;
}
tbl_size = sizeof(*tbl) + tbl->num_entries * tbl->desc_size;
memblock_reserve(efi.mem_attr_table, tbl_size);
unmap:
early_memunmap(tbl, sizeof(*tbl));
return 0;
}
/*
* Returns a copy @out of the UEFI memory descriptor @in if it is covered
* entirely by a UEFI memory map entry with matching attributes. The virtual
* address of @out is set according to the matching entry that was found.
*/
static bool entry_is_valid(const efi_memory_desc_t *in, efi_memory_desc_t *out)
{
u64 in_paddr = in->phys_addr;
u64 in_size = in->num_pages << EFI_PAGE_SHIFT;
efi_memory_desc_t *md;
*out = *in;
if (in->type != EFI_RUNTIME_SERVICES_CODE &&
in->type != EFI_RUNTIME_SERVICES_DATA) {
pr_warn("Entry type should be RuntimeServiceCode/Data\n");
return false;
}
if (!(in->attribute & (EFI_MEMORY_RO | EFI_MEMORY_XP))) {
pr_warn("Entry attributes invalid: RO and XP bits both cleared\n");
return false;
}
if (PAGE_SIZE > EFI_PAGE_SIZE &&
(!PAGE_ALIGNED(in->phys_addr) ||
!PAGE_ALIGNED(in->num_pages << EFI_PAGE_SHIFT))) {
/*
* Since arm64 may execute with page sizes of up to 64 KB, the
* UEFI spec mandates that RuntimeServices memory regions must
* be 64 KB aligned. We need to validate this here since we will
* not be able to tighten permissions on such regions without
* affecting adjacent regions.
*/
pr_warn("Entry address region misaligned\n");
return false;
}
for_each_efi_memory_desc(md) {
u64 md_paddr = md->phys_addr;
u64 md_size = md->num_pages << EFI_PAGE_SHIFT;
if (!(md->attribute & EFI_MEMORY_RUNTIME))
continue;
if (md->virt_addr == 0) {
/* no virtual mapping has been installed by the stub */
break;
}
if (md_paddr > in_paddr || (in_paddr - md_paddr) >= md_size)
continue;
/*
* This entry covers the start of @in, check whether
* it covers the end as well.
*/
if (md_paddr + md_size < in_paddr + in_size) {
pr_warn("Entry covers multiple EFI memory map regions\n");
return false;
}
if (md->type != in->type) {
pr_warn("Entry type deviates from EFI memory map region type\n");
return false;
}
out->virt_addr = in_paddr + (md->virt_addr - md_paddr);
return true;
}
pr_warn("No matching entry found in the EFI memory map\n");
return false;
}
/*
* To be called after the EFI page tables have been populated. If a memory
* attributes table is available, its contents will be used to update the
* mappings with tightened permissions as described by the table.
* This requires the UEFI memory map to have already been populated with
* virtual addresses.
*/
int __init efi_memattr_apply_permissions(struct mm_struct *mm,
efi_memattr_perm_setter fn)
{
efi_memory_attributes_table_t *tbl;
int i, ret;
if (tbl_size <= sizeof(*tbl))
return 0;
/*
* We need the EFI memory map to be setup so we can use it to
* lookup the virtual addresses of all entries in the of EFI
* Memory Attributes table. If it isn't available, this
* function should not be called.
*/
if (WARN_ON(!efi_enabled(EFI_MEMMAP)))
return 0;
tbl = memremap(efi.mem_attr_table, tbl_size, MEMREMAP_WB);
if (!tbl) {
pr_err("Failed to map EFI Memory Attributes table @ 0x%lx\n",
efi.mem_attr_table);
return -ENOMEM;
}
if (efi_enabled(EFI_DBG))
pr_info("Processing EFI Memory Attributes table:\n");
for (i = ret = 0; ret == 0 && i < tbl->num_entries; i++) {
efi_memory_desc_t md;
unsigned long size;
bool valid;
char buf[64];
valid = entry_is_valid((void *)tbl->entry + i * tbl->desc_size,
&md);
size = md.num_pages << EFI_PAGE_SHIFT;
if (efi_enabled(EFI_DBG) || !valid)
pr_info("%s 0x%012llx-0x%012llx %s\n",
valid ? "" : "!", md.phys_addr,
md.phys_addr + size - 1,
efi_md_typeattr_format(buf, sizeof(buf), &md));
if (valid)
ret = fn(mm, &md);
}
memunmap(tbl);
return ret;
}

View File

@ -9,7 +9,8 @@ int efi_reboot_quirk_mode = -1;
void efi_reboot(enum reboot_mode reboot_mode, const char *__unused) void efi_reboot(enum reboot_mode reboot_mode, const char *__unused)
{ {
int efi_mode; const char *str[] = { "cold", "warm", "shutdown", "platform" };
int efi_mode, cap_reset_mode;
if (!efi_enabled(EFI_RUNTIME_SERVICES)) if (!efi_enabled(EFI_RUNTIME_SERVICES))
return; return;
@ -30,6 +31,15 @@ void efi_reboot(enum reboot_mode reboot_mode, const char *__unused)
if (efi_reboot_quirk_mode != -1) if (efi_reboot_quirk_mode != -1)
efi_mode = efi_reboot_quirk_mode; efi_mode = efi_reboot_quirk_mode;
if (efi_capsule_pending(&cap_reset_mode)) {
if (efi_mode != cap_reset_mode)
printk(KERN_CRIT "efi: %s reset requested but pending "
"capsule update requires %s reset... Performing "
"%s reset.\n", str[efi_mode], str[cap_reset_mode],
str[cap_reset_mode]);
efi_mode = cap_reset_mode;
}
efi.reset_system(efi_mode, EFI_SUCCESS, 0, NULL); efi.reset_system(efi_mode, EFI_SUCCESS, 0, NULL);
} }

View File

@ -16,10 +16,70 @@
#include <linux/bug.h> #include <linux/bug.h>
#include <linux/efi.h> #include <linux/efi.h>
#include <linux/irqflags.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/stringify.h>
#include <asm/efi.h> #include <asm/efi.h>
static void efi_call_virt_check_flags(unsigned long flags, const char *call)
{
unsigned long cur_flags, mismatch;
local_save_flags(cur_flags);
mismatch = flags ^ cur_flags;
if (!WARN_ON_ONCE(mismatch & ARCH_EFI_IRQ_FLAGS_MASK))
return;
add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_NOW_UNRELIABLE);
pr_err_ratelimited(FW_BUG "IRQ flags corrupted (0x%08lx=>0x%08lx) by EFI %s\n",
flags, cur_flags, call);
local_irq_restore(flags);
}
/*
* Arch code can implement the following three template macros, avoiding
* reptition for the void/non-void return cases of {__,}efi_call_virt:
*
* * arch_efi_call_virt_setup
*
* Sets up the environment for the call (e.g. switching page tables,
* allowing kernel-mode use of floating point, if required).
*
* * arch_efi_call_virt
*
* Performs the call. The last expression in the macro must be the call
* itself, allowing the logic to be shared by the void and non-void
* cases.
*
* * arch_efi_call_virt_teardown
*
* Restores the usual kernel environment once the call has returned.
*/
#define efi_call_virt(f, args...) \
({ \
efi_status_t __s; \
unsigned long flags; \
arch_efi_call_virt_setup(); \
local_save_flags(flags); \
__s = arch_efi_call_virt(f, args); \
efi_call_virt_check_flags(flags, __stringify(f)); \
arch_efi_call_virt_teardown(); \
__s; \
})
#define __efi_call_virt(f, args...) \
({ \
unsigned long flags; \
arch_efi_call_virt_setup(); \
local_save_flags(flags); \
arch_efi_call_virt(f, args); \
efi_call_virt_check_flags(flags, __stringify(f)); \
arch_efi_call_virt_teardown(); \
})
/* /*
* According to section 7.1 of the UEFI spec, Runtime Services are not fully * According to section 7.1 of the UEFI spec, Runtime Services are not fully
* reentrant, and there are particular combinations of calls that need to be * reentrant, and there are particular combinations of calls that need to be

View File

@ -329,39 +329,6 @@ check_var_size_nonblocking(u32 attributes, unsigned long size)
return fops->query_variable_store(attributes, size, true); return fops->query_variable_store(attributes, size, true);
} }
static int efi_status_to_err(efi_status_t status)
{
int err;
switch (status) {
case EFI_SUCCESS:
err = 0;
break;
case EFI_INVALID_PARAMETER:
err = -EINVAL;
break;
case EFI_OUT_OF_RESOURCES:
err = -ENOSPC;
break;
case EFI_DEVICE_ERROR:
err = -EIO;
break;
case EFI_WRITE_PROTECTED:
err = -EROFS;
break;
case EFI_SECURITY_VIOLATION:
err = -EACCES;
break;
case EFI_NOT_FOUND:
err = -ENOENT;
break;
default:
err = -EINVAL;
}
return err;
}
static bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor, static bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor,
struct list_head *head) struct list_head *head)
{ {
@ -452,8 +419,7 @@ static void dup_variable_bug(efi_char16_t *str16, efi_guid_t *vendor_guid,
* Returns 0 on success, or a kernel error code on failure. * Returns 0 on success, or a kernel error code on failure.
*/ */
int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *), int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
void *data, bool atomic, bool duplicates, void *data, bool duplicates, struct list_head *head)
struct list_head *head)
{ {
const struct efivar_operations *ops = __efivars->ops; const struct efivar_operations *ops = __efivars->ops;
unsigned long variable_name_size = 1024; unsigned long variable_name_size = 1024;
@ -483,7 +449,7 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
&vendor_guid); &vendor_guid);
switch (status) { switch (status) {
case EFI_SUCCESS: case EFI_SUCCESS:
if (!atomic) if (duplicates)
spin_unlock_irq(&__efivars->lock); spin_unlock_irq(&__efivars->lock);
variable_name_size = var_name_strnsize(variable_name, variable_name_size = var_name_strnsize(variable_name,
@ -498,21 +464,19 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
* and may end up looping here forever. * and may end up looping here forever.
*/ */
if (duplicates && if (duplicates &&
variable_is_present(variable_name, &vendor_guid, head)) { variable_is_present(variable_name, &vendor_guid,
head)) {
dup_variable_bug(variable_name, &vendor_guid, dup_variable_bug(variable_name, &vendor_guid,
variable_name_size); variable_name_size);
if (!atomic)
spin_lock_irq(&__efivars->lock);
status = EFI_NOT_FOUND; status = EFI_NOT_FOUND;
break; } else {
err = func(variable_name, vendor_guid,
variable_name_size, data);
if (err)
status = EFI_NOT_FOUND;
} }
err = func(variable_name, vendor_guid, variable_name_size, data); if (duplicates)
if (err)
status = EFI_NOT_FOUND;
if (!atomic)
spin_lock_irq(&__efivars->lock); spin_lock_irq(&__efivars->lock);
break; break;

View File

@ -761,7 +761,7 @@ config FB_VESA
config FB_EFI config FB_EFI
bool "EFI-based Framebuffer Support" bool "EFI-based Framebuffer Support"
depends on (FB = y) && X86 && EFI depends on (FB = y) && !IA64 && EFI
select FB_CFB_FILLRECT select FB_CFB_FILLRECT
select FB_CFB_COPYAREA select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT select FB_CFB_IMAGEBLIT

View File

@ -6,16 +6,14 @@
* *
*/ */
#include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/efi.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/fb.h> #include <linux/fb.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/screen_info.h> #include <linux/screen_info.h>
#include <linux/dmi.h>
#include <linux/pci.h>
#include <video/vga.h> #include <video/vga.h>
#include <asm/sysfb.h> #include <asm/efi.h>
static bool request_mem_succeeded = false; static bool request_mem_succeeded = false;
@ -85,21 +83,13 @@ static struct fb_ops efifb_ops = {
static int efifb_setup(char *options) static int efifb_setup(char *options)
{ {
char *this_opt; char *this_opt;
int i;
if (options && *options) { if (options && *options) {
while ((this_opt = strsep(&options, ",")) != NULL) { while ((this_opt = strsep(&options, ",")) != NULL) {
if (!*this_opt) continue; if (!*this_opt) continue;
for (i = 0; i < M_UNKNOWN; i++) { efifb_setup_from_dmi(&screen_info, this_opt);
if (efifb_dmi_list[i].base != 0 &&
!strcmp(this_opt, efifb_dmi_list[i].optname)) {
screen_info.lfb_base = efifb_dmi_list[i].base;
screen_info.lfb_linelength = efifb_dmi_list[i].stride;
screen_info.lfb_width = efifb_dmi_list[i].width;
screen_info.lfb_height = efifb_dmi_list[i].height;
}
}
if (!strncmp(this_opt, "base:", 5)) if (!strncmp(this_opt, "base:", 5))
screen_info.lfb_base = simple_strtoul(this_opt+5, NULL, 0); screen_info.lfb_base = simple_strtoul(this_opt+5, NULL, 0);
else if (!strncmp(this_opt, "stride:", 7)) else if (!strncmp(this_opt, "stride:", 7))
@ -338,5 +328,4 @@ static struct platform_driver efifb_driver = {
.remove = efifb_remove, .remove = efifb_remove,
}; };
module_platform_driver(efifb_driver); builtin_platform_driver(efifb_driver);
MODULE_LICENSE("GPL");

View File

@ -316,7 +316,6 @@ static const struct efi efi_xen __initconst = {
.get_next_high_mono_count = xen_efi_get_next_high_mono_count, .get_next_high_mono_count = xen_efi_get_next_high_mono_count,
.reset_system = NULL, /* Functionality provided by Xen. */ .reset_system = NULL, /* Functionality provided by Xen. */
.set_virtual_address_map = NULL, /* Not used under Xen. */ .set_virtual_address_map = NULL, /* Not used under Xen. */
.memmap = NULL, /* Not used under Xen. */
.flags = 0 /* Initialized later. */ .flags = 0 /* Initialized later. */
}; };

View File

@ -157,7 +157,7 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
return 0; return 0;
} }
long static long
efivarfs_file_ioctl(struct file *file, unsigned int cmd, unsigned long p) efivarfs_file_ioctl(struct file *file, unsigned int cmd, unsigned long p)
{ {
void __user *arg = (void __user *)p; void __user *arg = (void __user *)p;

View File

@ -216,8 +216,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
INIT_LIST_HEAD(&efivarfs_list); INIT_LIST_HEAD(&efivarfs_list);
err = efivar_init(efivarfs_callback, (void *)sb, false, err = efivar_init(efivarfs_callback, (void *)sb, true, &efivarfs_list);
true, &efivarfs_list);
if (err) if (err)
__efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL, NULL); __efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL, NULL);

View File

@ -21,6 +21,7 @@
#include <linux/pfn.h> #include <linux/pfn.h>
#include <linux/pstore.h> #include <linux/pstore.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/screen_info.h>
#include <asm/page.h> #include <asm/page.h>
@ -123,6 +124,13 @@ typedef struct {
u32 imagesize; u32 imagesize;
} efi_capsule_header_t; } efi_capsule_header_t;
/*
* EFI capsule flags
*/
#define EFI_CAPSULE_PERSIST_ACROSS_RESET 0x00010000
#define EFI_CAPSULE_POPULATE_SYSTEM_TABLE 0x00020000
#define EFI_CAPSULE_INITIATE_RESET 0x00040000
/* /*
* Allocation types for calls to boottime->allocate_pages. * Allocation types for calls to boottime->allocate_pages.
*/ */
@ -282,9 +290,10 @@ typedef struct {
efi_status_t (*handle_protocol)(efi_handle_t, efi_guid_t *, void **); efi_status_t (*handle_protocol)(efi_handle_t, efi_guid_t *, void **);
void *__reserved; void *__reserved;
void *register_protocol_notify; void *register_protocol_notify;
void *locate_handle; efi_status_t (*locate_handle)(int, efi_guid_t *, void *,
unsigned long *, efi_handle_t *);
void *locate_device_path; void *locate_device_path;
void *install_configuration_table; efi_status_t (*install_configuration_table)(efi_guid_t *, void *);
void *load_image; void *load_image;
void *start_image; void *start_image;
void *exit; void *exit;
@ -623,6 +632,27 @@ void efi_native_runtime_setup(void);
EFI_GUID(0x3152bca5, 0xeade, 0x433d, \ EFI_GUID(0x3152bca5, 0xeade, 0x433d, \
0x86, 0x2e, 0xc0, 0x1c, 0xdc, 0x29, 0x1f, 0x44) 0x86, 0x2e, 0xc0, 0x1c, 0xdc, 0x29, 0x1f, 0x44)
#define EFI_MEMORY_ATTRIBUTES_TABLE_GUID \
EFI_GUID(0xdcfa911d, 0x26eb, 0x469f, \
0xa2, 0x20, 0x38, 0xb7, 0xdc, 0x46, 0x12, 0x20)
#define EFI_CONSOLE_OUT_DEVICE_GUID \
EFI_GUID(0xd3b36f2c, 0xd551, 0x11d4, \
0x9a, 0x46, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d)
/*
* This GUID is used to pass to the kernel proper the struct screen_info
* structure that was populated by the stub based on the GOP protocol instance
* associated with ConOut
*/
#define LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID \
EFI_GUID(0xe03fc20a, 0x85dc, 0x406e, \
0xb9, 0xe, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95)
#define LINUX_EFI_LOADER_ENTRY_GUID \
EFI_GUID(0x4a67b082, 0x0a4c, 0x41cf, \
0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f)
typedef struct { typedef struct {
efi_guid_t guid; efi_guid_t guid;
u64 table; u64 table;
@ -847,6 +877,14 @@ typedef struct {
#define EFI_INVALID_TABLE_ADDR (~0UL) #define EFI_INVALID_TABLE_ADDR (~0UL)
typedef struct {
u32 version;
u32 num_entries;
u32 desc_size;
u32 reserved;
efi_memory_desc_t entry[0];
} efi_memory_attributes_table_t;
/* /*
* All runtime access to EFI goes through this structure: * All runtime access to EFI goes through this structure:
*/ */
@ -868,6 +906,7 @@ extern struct efi {
unsigned long config_table; /* config tables */ unsigned long config_table; /* config tables */
unsigned long esrt; /* ESRT table */ unsigned long esrt; /* ESRT table */
unsigned long properties_table; /* properties table */ unsigned long properties_table; /* properties table */
unsigned long mem_attr_table; /* memory attributes table */
efi_get_time_t *get_time; efi_get_time_t *get_time;
efi_set_time_t *set_time; efi_set_time_t *set_time;
efi_get_wakeup_time_t *get_wakeup_time; efi_get_wakeup_time_t *get_wakeup_time;
@ -883,7 +922,7 @@ extern struct efi {
efi_get_next_high_mono_count_t *get_next_high_mono_count; efi_get_next_high_mono_count_t *get_next_high_mono_count;
efi_reset_system_t *reset_system; efi_reset_system_t *reset_system;
efi_set_virtual_address_map_t *set_virtual_address_map; efi_set_virtual_address_map_t *set_virtual_address_map;
struct efi_memory_map *memmap; struct efi_memory_map memmap;
unsigned long flags; unsigned long flags;
} efi; } efi;
@ -945,7 +984,6 @@ extern void efi_initialize_iomem_resources(struct resource *code_resource,
extern void efi_get_time(struct timespec *now); extern void efi_get_time(struct timespec *now);
extern void efi_reserve_boot_services(void); extern void efi_reserve_boot_services(void);
extern int efi_get_fdt_params(struct efi_fdt_params *params); extern int efi_get_fdt_params(struct efi_fdt_params *params);
extern struct efi_memory_map memmap;
extern struct kobject *efi_kobj; extern struct kobject *efi_kobj;
extern int efi_reboot_quirk_mode; extern int efi_reboot_quirk_mode;
@ -957,12 +995,34 @@ extern void __init efi_fake_memmap(void);
static inline void efi_fake_memmap(void) { } static inline void efi_fake_memmap(void) { }
#endif #endif
/*
* efi_memattr_perm_setter - arch specific callback function passed into
* efi_memattr_apply_permissions() that updates the
* mapping permissions described by the second
* argument in the page tables referred to by the
* first argument.
*/
typedef int (*efi_memattr_perm_setter)(struct mm_struct *, efi_memory_desc_t *);
extern int efi_memattr_init(void);
extern int efi_memattr_apply_permissions(struct mm_struct *mm,
efi_memattr_perm_setter fn);
/* Iterate through an efi_memory_map */ /* Iterate through an efi_memory_map */
#define for_each_efi_memory_desc(m, md) \ #define for_each_efi_memory_desc_in_map(m, md) \
for ((md) = (m)->map; \ for ((md) = (m)->map; \
(md) <= (efi_memory_desc_t *)((m)->map_end - (m)->desc_size); \ (md) <= (efi_memory_desc_t *)((m)->map_end - (m)->desc_size); \
(md) = (void *)(md) + (m)->desc_size) (md) = (void *)(md) + (m)->desc_size)
/**
* for_each_efi_memory_desc - iterate over descriptors in efi.memmap
* @md: the efi_memory_desc_t * iterator
*
* Once the loop finishes @md must not be accessed.
*/
#define for_each_efi_memory_desc(md) \
for_each_efi_memory_desc_in_map(&efi.memmap, md)
/* /*
* Format an EFI memory descriptor's type and attributes to a user-provided * Format an EFI memory descriptor's type and attributes to a user-provided
* character buffer, as per snprintf(), and return the buffer. * character buffer, as per snprintf(), and return the buffer.
@ -1000,7 +1060,6 @@ extern int __init efi_setup_pcdp_console(char *);
* possible, remove EFI-related code altogether. * possible, remove EFI-related code altogether.
*/ */
#define EFI_BOOT 0 /* Were we booted from EFI? */ #define EFI_BOOT 0 /* Were we booted from EFI? */
#define EFI_SYSTEM_TABLES 1 /* Can we use EFI system tables? */
#define EFI_CONFIG_TABLES 2 /* Can we use EFI config tables? */ #define EFI_CONFIG_TABLES 2 /* Can we use EFI config tables? */
#define EFI_RUNTIME_SERVICES 3 /* Can we use runtime services? */ #define EFI_RUNTIME_SERVICES 3 /* Can we use runtime services? */
#define EFI_MEMMAP 4 /* Can we use EFI memory map? */ #define EFI_MEMMAP 4 /* Can we use EFI memory map? */
@ -1026,8 +1085,16 @@ static inline bool efi_enabled(int feature)
} }
static inline void static inline void
efi_reboot(enum reboot_mode reboot_mode, const char *__unused) {} efi_reboot(enum reboot_mode reboot_mode, const char *__unused) {}
static inline bool
efi_capsule_pending(int *reset_type)
{
return false;
}
#endif #endif
extern int efi_status_to_err(efi_status_t status);
/* /*
* Variable Attributes * Variable Attributes
*/ */
@ -1180,6 +1247,80 @@ struct efi_simple_text_output_protocol {
void *test_string; void *test_string;
}; };
#define PIXEL_RGB_RESERVED_8BIT_PER_COLOR 0
#define PIXEL_BGR_RESERVED_8BIT_PER_COLOR 1
#define PIXEL_BIT_MASK 2
#define PIXEL_BLT_ONLY 3
#define PIXEL_FORMAT_MAX 4
struct efi_pixel_bitmask {
u32 red_mask;
u32 green_mask;
u32 blue_mask;
u32 reserved_mask;
};
struct efi_graphics_output_mode_info {
u32 version;
u32 horizontal_resolution;
u32 vertical_resolution;
int pixel_format;
struct efi_pixel_bitmask pixel_information;
u32 pixels_per_scan_line;
} __packed;
struct efi_graphics_output_protocol_mode_32 {
u32 max_mode;
u32 mode;
u32 info;
u32 size_of_info;
u64 frame_buffer_base;
u32 frame_buffer_size;
} __packed;
struct efi_graphics_output_protocol_mode_64 {
u32 max_mode;
u32 mode;
u64 info;
u64 size_of_info;
u64 frame_buffer_base;
u64 frame_buffer_size;
} __packed;
struct efi_graphics_output_protocol_mode {
u32 max_mode;
u32 mode;
unsigned long info;
unsigned long size_of_info;
u64 frame_buffer_base;
unsigned long frame_buffer_size;
} __packed;
struct efi_graphics_output_protocol_32 {
u32 query_mode;
u32 set_mode;
u32 blt;
u32 mode;
};
struct efi_graphics_output_protocol_64 {
u64 query_mode;
u64 set_mode;
u64 blt;
u64 mode;
};
struct efi_graphics_output_protocol {
unsigned long query_mode;
unsigned long set_mode;
unsigned long blt;
struct efi_graphics_output_protocol_mode *mode;
};
typedef efi_status_t (*efi_graphics_output_protocol_query_mode)(
struct efi_graphics_output_protocol *, u32, unsigned long *,
struct efi_graphics_output_mode_info **);
extern struct list_head efivar_sysfs_list; extern struct list_head efivar_sysfs_list;
static inline void static inline void
@ -1195,8 +1336,7 @@ int efivars_unregister(struct efivars *efivars);
struct kobject *efivars_kobject(void); struct kobject *efivars_kobject(void);
int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *), int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
void *data, bool atomic, bool duplicates, void *data, bool duplicates, struct list_head *head);
struct list_head *head);
void efivar_entry_add(struct efivar_entry *entry, struct list_head *head); void efivar_entry_add(struct efivar_entry *entry, struct list_head *head);
void efivar_entry_remove(struct efivar_entry *entry); void efivar_entry_remove(struct efivar_entry *entry);
@ -1242,6 +1382,13 @@ int efivars_sysfs_init(void);
#define EFIVARS_DATA_SIZE_MAX 1024 #define EFIVARS_DATA_SIZE_MAX 1024
#endif /* CONFIG_EFI_VARS */ #endif /* CONFIG_EFI_VARS */
extern bool efi_capsule_pending(int *reset_type);
extern int efi_capsule_supported(efi_guid_t guid, u32 flags,
size_t size, int *reset);
extern int efi_capsule_update(efi_capsule_header_t *capsule,
struct page **pages);
#ifdef CONFIG_EFI_RUNTIME_MAP #ifdef CONFIG_EFI_RUNTIME_MAP
int efi_runtime_map_init(struct kobject *); int efi_runtime_map_init(struct kobject *);
@ -1319,5 +1466,9 @@ efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
efi_status_t efi_parse_options(char *cmdline); efi_status_t efi_parse_options(char *cmdline);
efi_status_t efi_setup_gop(efi_system_table_t *sys_table_arg,
struct screen_info *si, efi_guid_t *proto,
unsigned long size);
bool efi_runtime_disabled(void); bool efi_runtime_disabled(void);
#endif /* _LINUX_EFI_H */ #endif /* _LINUX_EFI_H */