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:
commit
49817c3343
|
@ -17,33 +17,27 @@
|
|||
#include <asm/mach/map.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/ptrace.h>
|
||||
|
||||
#ifdef CONFIG_EFI
|
||||
void efi_init(void);
|
||||
|
||||
int efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md);
|
||||
int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md);
|
||||
|
||||
#define efi_call_virt(f, ...) \
|
||||
#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_status_t __s; \
|
||||
\
|
||||
efi_virtmap_load(); \
|
||||
__f = efi.systab->runtime->f; \
|
||||
__s = __f(__VA_ARGS__); \
|
||||
efi_virtmap_unload(); \
|
||||
__s; \
|
||||
__f(args); \
|
||||
})
|
||||
|
||||
#define __efi_call_virt(f, ...) \
|
||||
({ \
|
||||
efi_##f##_t *__f; \
|
||||
\
|
||||
efi_virtmap_load(); \
|
||||
__f = efi.systab->runtime->f; \
|
||||
__f(__VA_ARGS__); \
|
||||
efi_virtmap_unload(); \
|
||||
})
|
||||
#define ARCH_EFI_IRQ_FLAGS_MASK \
|
||||
(PSR_J_BIT | PSR_E_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT | \
|
||||
PSR_T_BIT | MODE_MASK)
|
||||
|
||||
static inline void efi_set_pgd(struct mm_struct *mm)
|
||||
{
|
||||
|
@ -60,6 +54,15 @@ void efi_virtmap_unload(void);
|
|||
/* arch specific definitions used by the stub code */
|
||||
|
||||
#define efi_call_early(f, ...) sys_table_arg->boottime->f(__VA_ARGS__)
|
||||
#define __efi_call_early(f, ...) 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,
|
||||
|
|
|
@ -11,6 +11,41 @@
|
|||
#include <asm/mach/map.h>
|
||||
#include <asm/mmu_context.h>
|
||||
|
||||
static int __init set_permissions(pte_t *ptep, pgtable_t token,
|
||||
unsigned long addr, void *data)
|
||||
{
|
||||
efi_memory_desc_t *md = data;
|
||||
pte_t pte = *ptep;
|
||||
|
||||
if (md->attribute & EFI_MEMORY_RO)
|
||||
pte = set_pte_bit(pte, __pgprot(L_PTE_RDONLY));
|
||||
if (md->attribute & EFI_MEMORY_XP)
|
||||
pte = set_pte_bit(pte, __pgprot(L_PTE_XN));
|
||||
set_pte_ext(ptep, pte, PTE_EXT_NG);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init efi_set_mapping_permissions(struct mm_struct *mm,
|
||||
efi_memory_desc_t *md)
|
||||
{
|
||||
unsigned long base, size;
|
||||
|
||||
base = md->virt_addr;
|
||||
size = md->num_pages << EFI_PAGE_SHIFT;
|
||||
|
||||
/*
|
||||
* We can only use apply_to_page_range() if we can guarantee that the
|
||||
* entire region was mapped using pages. This should be the case if the
|
||||
* region does not cover any naturally aligned SECTION_SIZE sized
|
||||
* blocks.
|
||||
*/
|
||||
if (round_down(base + size, SECTION_SIZE) <
|
||||
round_up(base, SECTION_SIZE) + SECTION_SIZE)
|
||||
return apply_to_page_range(mm, base, size, set_permissions, md);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md)
|
||||
{
|
||||
struct map_desc desc = {
|
||||
|
@ -34,5 +69,11 @@ int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md)
|
|||
desc.type = MT_DEVICE;
|
||||
|
||||
create_mapping_late(mm, &desc, true);
|
||||
|
||||
/*
|
||||
* If stricter permissions were specified, apply them now.
|
||||
*/
|
||||
if (md->attribute & (EFI_MEMORY_RO | EFI_MEMORY_XP))
|
||||
return efi_set_mapping_permissions(mm, md);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -883,7 +883,8 @@ static void __init request_standard_resources(const struct machine_desc *mdesc)
|
|||
request_resource(&ioport_resource, &lp2);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE)
|
||||
#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE) || \
|
||||
defined(CONFIG_EFI)
|
||||
struct screen_info screen_info = {
|
||||
.orig_video_lines = 30,
|
||||
.orig_video_cols = 80,
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <asm/io.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/neon.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/tlbflush.h>
|
||||
|
||||
#ifdef CONFIG_EFI
|
||||
|
@ -14,32 +15,29 @@ extern void efi_init(void);
|
|||
|
||||
int efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md);
|
||||
|
||||
#define efi_call_virt(f, ...) \
|
||||
#define efi_set_mapping_permissions efi_create_mapping
|
||||
|
||||
#define arch_efi_call_virt_setup() \
|
||||
({ \
|
||||
efi_##f##_t *__f; \
|
||||
efi_status_t __s; \
|
||||
\
|
||||
kernel_neon_begin(); \
|
||||
efi_virtmap_load(); \
|
||||
__f = efi.systab->runtime->f; \
|
||||
__s = __f(__VA_ARGS__); \
|
||||
efi_virtmap_unload(); \
|
||||
kernel_neon_end(); \
|
||||
__s; \
|
||||
})
|
||||
|
||||
#define __efi_call_virt(f, ...) \
|
||||
#define arch_efi_call_virt(f, args...) \
|
||||
({ \
|
||||
efi_##f##_t *__f; \
|
||||
\
|
||||
kernel_neon_begin(); \
|
||||
efi_virtmap_load(); \
|
||||
__f = efi.systab->runtime->f; \
|
||||
__f(__VA_ARGS__); \
|
||||
__f(args); \
|
||||
})
|
||||
|
||||
#define arch_efi_call_virt_teardown() \
|
||||
({ \
|
||||
efi_virtmap_unload(); \
|
||||
kernel_neon_end(); \
|
||||
})
|
||||
|
||||
#define ARCH_EFI_IRQ_FLAGS_MASK (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT)
|
||||
|
||||
/* arch specific definitions used by the stub code */
|
||||
|
||||
/*
|
||||
|
@ -51,6 +49,15 @@ int efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md);
|
|||
#define MAX_FDT_OFFSET SZ_512M
|
||||
|
||||
#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
|
||||
|
||||
|
|
|
@ -17,22 +17,51 @@
|
|||
|
||||
#include <asm/efi.h>
|
||||
|
||||
int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md)
|
||||
{
|
||||
pteval_t prot_val;
|
||||
|
||||
/*
|
||||
/*
|
||||
* Only regions of type EFI_RUNTIME_SERVICES_CODE need to be
|
||||
* executable, everything else can be mapped with the XN bits
|
||||
* set.
|
||||
* set. Also take the new (optional) RO/XP bits into account.
|
||||
*/
|
||||
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);
|
||||
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)
|
||||
{
|
||||
pteval_t prot_val = create_mapping_protection(md);
|
||||
|
||||
create_pgd_mapping(mm, md->phys_addr, md->virt_addr,
|
||||
md->num_pages << EFI_PAGE_SHIFT,
|
||||
|
|
|
@ -112,6 +112,7 @@ __efistub___memset = KALLSYMS_HIDE(__pi_memset);
|
|||
__efistub__text = KALLSYMS_HIDE(_text);
|
||||
__efistub__end = KALLSYMS_HIDE(_end);
|
||||
__efistub__edata = KALLSYMS_HIDE(_edata);
|
||||
__efistub_screen_info = KALLSYMS_HIDE(screen_info);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -531,8 +531,6 @@ efi_init (void)
|
|||
efi.systab->hdr.revision >> 16,
|
||||
efi.systab->hdr.revision & 0xffff, vendor);
|
||||
|
||||
set_bit(EFI_SYSTEM_TABLES, &efi.flags);
|
||||
|
||||
palo_phys = EFI_INVALID_TABLE_ADDR;
|
||||
|
||||
if (efi_config_init(arch_tables) != 0)
|
||||
|
|
|
@ -571,312 +571,6 @@ free_handle:
|
|||
efi_call_early(free_pool, pci_handle);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
|
||||
struct efi_pixel_bitmask pixel_info, int pixel_format)
|
||||
{
|
||||
if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
|
||||
si->lfb_depth = 32;
|
||||
si->lfb_linelength = pixels_per_scan_line * 4;
|
||||
si->red_size = 8;
|
||||
si->red_pos = 0;
|
||||
si->green_size = 8;
|
||||
si->green_pos = 8;
|
||||
si->blue_size = 8;
|
||||
si->blue_pos = 16;
|
||||
si->rsvd_size = 8;
|
||||
si->rsvd_pos = 24;
|
||||
} else if (pixel_format == PIXEL_BGR_RESERVED_8BIT_PER_COLOR) {
|
||||
si->lfb_depth = 32;
|
||||
si->lfb_linelength = pixels_per_scan_line * 4;
|
||||
si->red_size = 8;
|
||||
si->red_pos = 16;
|
||||
si->green_size = 8;
|
||||
si->green_pos = 8;
|
||||
si->blue_size = 8;
|
||||
si->blue_pos = 0;
|
||||
si->rsvd_size = 8;
|
||||
si->rsvd_pos = 24;
|
||||
} else if (pixel_format == PIXEL_BIT_MASK) {
|
||||
find_bits(pixel_info.red_mask, &si->red_pos, &si->red_size);
|
||||
find_bits(pixel_info.green_mask, &si->green_pos,
|
||||
&si->green_size);
|
||||
find_bits(pixel_info.blue_mask, &si->blue_pos, &si->blue_size);
|
||||
find_bits(pixel_info.reserved_mask, &si->rsvd_pos,
|
||||
&si->rsvd_size);
|
||||
si->lfb_depth = si->red_size + si->green_size +
|
||||
si->blue_size + si->rsvd_size;
|
||||
si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
|
||||
} else {
|
||||
si->lfb_depth = 4;
|
||||
si->lfb_linelength = si->lfb_width / 2;
|
||||
si->red_size = 0;
|
||||
si->red_pos = 0;
|
||||
si->green_size = 0;
|
||||
si->green_pos = 0;
|
||||
si->blue_size = 0;
|
||||
si->blue_pos = 0;
|
||||
si->rsvd_size = 0;
|
||||
si->rsvd_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static efi_status_t
|
||||
__gop_query32(struct efi_graphics_output_protocol_32 *gop32,
|
||||
struct efi_graphics_output_mode_info **info,
|
||||
unsigned long *size, u64 *fb_base)
|
||||
{
|
||||
struct efi_graphics_output_protocol_mode_32 *mode;
|
||||
efi_status_t status;
|
||||
unsigned long m;
|
||||
|
||||
m = gop32->mode;
|
||||
mode = (struct efi_graphics_output_protocol_mode_32 *)m;
|
||||
|
||||
status = efi_early->call(gop32->query_mode, gop32,
|
||||
mode->mode, size, info);
|
||||
if (status != EFI_SUCCESS)
|
||||
return status;
|
||||
|
||||
*fb_base = mode->frame_buffer_base;
|
||||
return status;
|
||||
}
|
||||
|
||||
static efi_status_t
|
||||
setup_gop32(struct screen_info *si, efi_guid_t *proto,
|
||||
unsigned long size, void **gop_handle)
|
||||
{
|
||||
struct efi_graphics_output_protocol_32 *gop32, *first_gop;
|
||||
unsigned long nr_gops;
|
||||
u16 width, height;
|
||||
u32 pixels_per_scan_line;
|
||||
u32 ext_lfb_base;
|
||||
u64 fb_base;
|
||||
struct efi_pixel_bitmask pixel_info;
|
||||
int pixel_format;
|
||||
efi_status_t status;
|
||||
u32 *handles = (u32 *)(unsigned long)gop_handle;
|
||||
int i;
|
||||
|
||||
first_gop = NULL;
|
||||
gop32 = NULL;
|
||||
|
||||
nr_gops = size / sizeof(u32);
|
||||
for (i = 0; i < nr_gops; i++) {
|
||||
struct efi_graphics_output_mode_info *info = NULL;
|
||||
efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
|
||||
bool conout_found = false;
|
||||
void *dummy = NULL;
|
||||
u32 h = handles[i];
|
||||
u64 current_fb_base;
|
||||
|
||||
status = efi_call_early(handle_protocol, h,
|
||||
proto, (void **)&gop32);
|
||||
if (status != EFI_SUCCESS)
|
||||
continue;
|
||||
|
||||
status = efi_call_early(handle_protocol, h,
|
||||
&conout_proto, &dummy);
|
||||
if (status == EFI_SUCCESS)
|
||||
conout_found = true;
|
||||
|
||||
status = __gop_query32(gop32, &info, &size, ¤t_fb_base);
|
||||
if (status == EFI_SUCCESS && (!first_gop || conout_found)) {
|
||||
/*
|
||||
* Systems that use the UEFI Console Splitter may
|
||||
* provide multiple GOP devices, not all of which are
|
||||
* backed by real hardware. The workaround is to search
|
||||
* for a GOP implementing the ConOut protocol, and if
|
||||
* one isn't found, to just fall back to the first GOP.
|
||||
*/
|
||||
width = info->horizontal_resolution;
|
||||
height = info->vertical_resolution;
|
||||
pixel_format = info->pixel_format;
|
||||
pixel_info = info->pixel_information;
|
||||
pixels_per_scan_line = info->pixels_per_scan_line;
|
||||
fb_base = current_fb_base;
|
||||
|
||||
/*
|
||||
* Once we've found a GOP supporting ConOut,
|
||||
* don't bother looking any further.
|
||||
*/
|
||||
first_gop = gop32;
|
||||
if (conout_found)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Did we find any GOPs? */
|
||||
if (!first_gop)
|
||||
goto out;
|
||||
|
||||
/* EFI framebuffer */
|
||||
si->orig_video_isVGA = VIDEO_TYPE_EFI;
|
||||
|
||||
si->lfb_width = width;
|
||||
si->lfb_height = height;
|
||||
si->lfb_base = fb_base;
|
||||
|
||||
ext_lfb_base = (u64)(unsigned long)fb_base >> 32;
|
||||
if (ext_lfb_base) {
|
||||
si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
|
||||
si->ext_lfb_base = ext_lfb_base;
|
||||
}
|
||||
|
||||
si->pages = 1;
|
||||
|
||||
setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format);
|
||||
|
||||
si->lfb_size = si->lfb_linelength * si->lfb_height;
|
||||
|
||||
si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static efi_status_t
|
||||
__gop_query64(struct efi_graphics_output_protocol_64 *gop64,
|
||||
struct efi_graphics_output_mode_info **info,
|
||||
unsigned long *size, u64 *fb_base)
|
||||
{
|
||||
struct efi_graphics_output_protocol_mode_64 *mode;
|
||||
efi_status_t status;
|
||||
unsigned long m;
|
||||
|
||||
m = gop64->mode;
|
||||
mode = (struct efi_graphics_output_protocol_mode_64 *)m;
|
||||
|
||||
status = efi_early->call(gop64->query_mode, gop64,
|
||||
mode->mode, size, info);
|
||||
if (status != EFI_SUCCESS)
|
||||
return status;
|
||||
|
||||
*fb_base = mode->frame_buffer_base;
|
||||
return status;
|
||||
}
|
||||
|
||||
static efi_status_t
|
||||
setup_gop64(struct screen_info *si, efi_guid_t *proto,
|
||||
unsigned long size, void **gop_handle)
|
||||
{
|
||||
struct efi_graphics_output_protocol_64 *gop64, *first_gop;
|
||||
unsigned long nr_gops;
|
||||
u16 width, height;
|
||||
u32 pixels_per_scan_line;
|
||||
u32 ext_lfb_base;
|
||||
u64 fb_base;
|
||||
struct efi_pixel_bitmask pixel_info;
|
||||
int pixel_format;
|
||||
efi_status_t status;
|
||||
u64 *handles = (u64 *)(unsigned long)gop_handle;
|
||||
int i;
|
||||
|
||||
first_gop = NULL;
|
||||
gop64 = NULL;
|
||||
|
||||
nr_gops = size / sizeof(u64);
|
||||
for (i = 0; i < nr_gops; i++) {
|
||||
struct efi_graphics_output_mode_info *info = NULL;
|
||||
efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
|
||||
bool conout_found = false;
|
||||
void *dummy = NULL;
|
||||
u64 h = handles[i];
|
||||
u64 current_fb_base;
|
||||
|
||||
status = efi_call_early(handle_protocol, h,
|
||||
proto, (void **)&gop64);
|
||||
if (status != EFI_SUCCESS)
|
||||
continue;
|
||||
|
||||
status = efi_call_early(handle_protocol, h,
|
||||
&conout_proto, &dummy);
|
||||
if (status == EFI_SUCCESS)
|
||||
conout_found = true;
|
||||
|
||||
status = __gop_query64(gop64, &info, &size, ¤t_fb_base);
|
||||
if (status == EFI_SUCCESS && (!first_gop || conout_found)) {
|
||||
/*
|
||||
* Systems that use the UEFI Console Splitter may
|
||||
* provide multiple GOP devices, not all of which are
|
||||
* backed by real hardware. The workaround is to search
|
||||
* for a GOP implementing the ConOut protocol, and if
|
||||
* one isn't found, to just fall back to the first GOP.
|
||||
*/
|
||||
width = info->horizontal_resolution;
|
||||
height = info->vertical_resolution;
|
||||
pixel_format = info->pixel_format;
|
||||
pixel_info = info->pixel_information;
|
||||
pixels_per_scan_line = info->pixels_per_scan_line;
|
||||
fb_base = current_fb_base;
|
||||
|
||||
/*
|
||||
* Once we've found a GOP supporting ConOut,
|
||||
* don't bother looking any further.
|
||||
*/
|
||||
first_gop = gop64;
|
||||
if (conout_found)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Did we find any GOPs? */
|
||||
if (!first_gop)
|
||||
goto out;
|
||||
|
||||
/* EFI framebuffer */
|
||||
si->orig_video_isVGA = VIDEO_TYPE_EFI;
|
||||
|
||||
si->lfb_width = width;
|
||||
si->lfb_height = height;
|
||||
si->lfb_base = fb_base;
|
||||
|
||||
ext_lfb_base = (u64)(unsigned long)fb_base >> 32;
|
||||
if (ext_lfb_base) {
|
||||
si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
|
||||
si->ext_lfb_base = ext_lfb_base;
|
||||
}
|
||||
|
||||
si->pages = 1;
|
||||
|
||||
setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format);
|
||||
|
||||
si->lfb_size = si->lfb_linelength * si->lfb_height;
|
||||
|
||||
si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* See if we have Graphics Output Protocol
|
||||
*/
|
||||
static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
|
||||
unsigned long size)
|
||||
{
|
||||
efi_status_t status;
|
||||
void **gop_handle = NULL;
|
||||
|
||||
status = efi_call_early(allocate_pool, EFI_LOADER_DATA,
|
||||
size, (void **)&gop_handle);
|
||||
if (status != EFI_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = efi_call_early(locate_handle,
|
||||
EFI_LOCATE_BY_PROTOCOL,
|
||||
proto, NULL, &size, gop_handle);
|
||||
if (status != EFI_SUCCESS)
|
||||
goto free_handle;
|
||||
|
||||
if (efi_early->is64)
|
||||
status = setup_gop64(si, proto, size, gop_handle);
|
||||
else
|
||||
status = setup_gop32(si, proto, size, gop_handle);
|
||||
|
||||
free_handle:
|
||||
efi_call_early(free_pool, gop_handle);
|
||||
return status;
|
||||
}
|
||||
|
||||
static efi_status_t
|
||||
setup_uga32(void **uga_handle, unsigned long size, u32 *width, u32 *height)
|
||||
{
|
||||
|
@ -1038,7 +732,7 @@ void setup_graphics(struct boot_params *boot_params)
|
|||
EFI_LOCATE_BY_PROTOCOL,
|
||||
&graphics_proto, NULL, &size, gop_handle);
|
||||
if (status == EFI_BUFFER_TOO_SMALL)
|
||||
status = setup_gop(si, &graphics_proto, size);
|
||||
status = efi_setup_gop(NULL, si, &graphics_proto, size);
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
size = 0;
|
||||
|
|
|
@ -11,80 +11,6 @@
|
|||
|
||||
#define DESC_TYPE_CODE_DATA (1 << 0)
|
||||
|
||||
#define EFI_CONSOLE_OUT_DEVICE_GUID \
|
||||
EFI_GUID(0xd3b36f2c, 0xd551, 0x11d4, 0x9a, 0x46, 0x0, 0x90, 0x27, \
|
||||
0x3f, 0xc1, 0x4d)
|
||||
|
||||
#define PIXEL_RGB_RESERVED_8BIT_PER_COLOR 0
|
||||
#define PIXEL_BGR_RESERVED_8BIT_PER_COLOR 1
|
||||
#define PIXEL_BIT_MASK 2
|
||||
#define PIXEL_BLT_ONLY 3
|
||||
#define PIXEL_FORMAT_MAX 4
|
||||
|
||||
struct efi_pixel_bitmask {
|
||||
u32 red_mask;
|
||||
u32 green_mask;
|
||||
u32 blue_mask;
|
||||
u32 reserved_mask;
|
||||
};
|
||||
|
||||
struct efi_graphics_output_mode_info {
|
||||
u32 version;
|
||||
u32 horizontal_resolution;
|
||||
u32 vertical_resolution;
|
||||
int pixel_format;
|
||||
struct efi_pixel_bitmask pixel_information;
|
||||
u32 pixels_per_scan_line;
|
||||
} __packed;
|
||||
|
||||
struct efi_graphics_output_protocol_mode_32 {
|
||||
u32 max_mode;
|
||||
u32 mode;
|
||||
u32 info;
|
||||
u32 size_of_info;
|
||||
u64 frame_buffer_base;
|
||||
u32 frame_buffer_size;
|
||||
} __packed;
|
||||
|
||||
struct efi_graphics_output_protocol_mode_64 {
|
||||
u32 max_mode;
|
||||
u32 mode;
|
||||
u64 info;
|
||||
u64 size_of_info;
|
||||
u64 frame_buffer_base;
|
||||
u64 frame_buffer_size;
|
||||
} __packed;
|
||||
|
||||
struct efi_graphics_output_protocol_mode {
|
||||
u32 max_mode;
|
||||
u32 mode;
|
||||
unsigned long info;
|
||||
unsigned long size_of_info;
|
||||
u64 frame_buffer_base;
|
||||
unsigned long frame_buffer_size;
|
||||
} __packed;
|
||||
|
||||
struct efi_graphics_output_protocol_32 {
|
||||
u32 query_mode;
|
||||
u32 set_mode;
|
||||
u32 blt;
|
||||
u32 mode;
|
||||
};
|
||||
|
||||
struct efi_graphics_output_protocol_64 {
|
||||
u64 query_mode;
|
||||
u64 set_mode;
|
||||
u64 blt;
|
||||
u64 mode;
|
||||
};
|
||||
|
||||
struct efi_graphics_output_protocol {
|
||||
void *query_mode;
|
||||
unsigned long set_mode;
|
||||
unsigned long blt;
|
||||
struct efi_graphics_output_protocol_mode *mode;
|
||||
};
|
||||
|
||||
struct efi_uga_draw_protocol_32 {
|
||||
u32 get_mode;
|
||||
u32 set_mode;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <asm/fpu/api.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/processor-flags.h>
|
||||
#include <asm/tlb.h>
|
||||
|
||||
/*
|
||||
|
@ -28,33 +29,22 @@
|
|||
|
||||
#define MAX_CMDLINE_ADDRESS UINT_MAX
|
||||
|
||||
#define ARCH_EFI_IRQ_FLAGS_MASK X86_EFLAGS_IF
|
||||
|
||||
#ifdef CONFIG_X86_32
|
||||
|
||||
|
||||
extern unsigned long asmlinkage efi_call_phys(void *, ...);
|
||||
|
||||
#define arch_efi_call_virt_setup() kernel_fpu_begin()
|
||||
#define arch_efi_call_virt_teardown() kernel_fpu_end()
|
||||
|
||||
/*
|
||||
* Wrap all the virtual calls in a way that forces the parameters on the stack.
|
||||
*/
|
||||
|
||||
/* Use this macro if your virtual returns a non-void value */
|
||||
#define efi_call_virt(f, args...) \
|
||||
#define arch_efi_call_virt(f, args...) \
|
||||
({ \
|
||||
efi_status_t __s; \
|
||||
kernel_fpu_begin(); \
|
||||
__s = ((efi_##f##_t __attribute__((regparm(0)))*) \
|
||||
efi.systab->runtime->f)(args); \
|
||||
kernel_fpu_end(); \
|
||||
__s; \
|
||||
})
|
||||
|
||||
/* Use this macro if your virtual call does not return any value */
|
||||
#define __efi_call_virt(f, args...) \
|
||||
({ \
|
||||
kernel_fpu_begin(); \
|
||||
((efi_##f##_t __attribute__((regparm(0)))*) \
|
||||
efi.systab->runtime->f)(args); \
|
||||
kernel_fpu_end(); \
|
||||
})
|
||||
|
||||
#define efi_ioremap(addr, size, type, attr) ioremap_cache(addr, size)
|
||||
|
@ -78,10 +68,8 @@ struct efi_scratch {
|
|||
u64 phys_stack;
|
||||
} __packed;
|
||||
|
||||
#define efi_call_virt(f, ...) \
|
||||
#define arch_efi_call_virt_setup() \
|
||||
({ \
|
||||
efi_status_t __s; \
|
||||
\
|
||||
efi_sync_low_kernel_mappings(); \
|
||||
preempt_disable(); \
|
||||
__kernel_fpu_begin(); \
|
||||
|
@ -91,9 +79,13 @@ struct efi_scratch {
|
|||
write_cr3((unsigned long)efi_scratch.efi_pgt); \
|
||||
__flush_tlb_all(); \
|
||||
} \
|
||||
\
|
||||
__s = efi_call((void *)efi.systab->runtime->f, __VA_ARGS__); \
|
||||
\
|
||||
})
|
||||
|
||||
#define arch_efi_call_virt(f, args...) \
|
||||
efi_call((void *)efi.systab->runtime->f, args) \
|
||||
|
||||
#define arch_efi_call_virt_teardown() \
|
||||
({ \
|
||||
if (efi_scratch.use_pgd) { \
|
||||
write_cr3(efi_scratch.prev_cr3); \
|
||||
__flush_tlb_all(); \
|
||||
|
@ -101,15 +93,8 @@ struct efi_scratch {
|
|||
\
|
||||
__kernel_fpu_end(); \
|
||||
preempt_enable(); \
|
||||
__s; \
|
||||
})
|
||||
|
||||
/*
|
||||
* All X86_64 virt calls return non-void values. Thus, use non-void call for
|
||||
* virt calls that would be void on X86_32.
|
||||
*/
|
||||
#define __efi_call_virt(f, args...) efi_call_virt(f, args)
|
||||
|
||||
extern void __iomem *__init efi_ioremap(unsigned long addr, unsigned long size,
|
||||
u32 type, u64 attribute);
|
||||
|
||||
|
@ -180,6 +165,8 @@ static inline bool efi_runtime_supported(void)
|
|||
extern struct console early_efi_console;
|
||||
extern void parse_efi_setup(u64 phys_addr, u32 data_len);
|
||||
|
||||
extern void efifb_setup_from_dmi(struct screen_info *si, const char *opt);
|
||||
|
||||
#ifdef CONFIG_EFI_MIXED
|
||||
extern void efi_thunk_runtime_setup(void);
|
||||
extern efi_status_t efi_thunk_set_virtual_address_map(
|
||||
|
@ -225,6 +212,11 @@ __pure const struct efi_config *__efi_early(void);
|
|||
#define efi_call_early(f, ...) \
|
||||
__efi_early()->call(__efi_early()->f, __VA_ARGS__);
|
||||
|
||||
#define __efi_call_early(f, ...) \
|
||||
__efi_early()->call((unsigned long)f, __VA_ARGS__);
|
||||
|
||||
#define efi_is_64bit() __efi_early()->is64
|
||||
|
||||
extern bool efi_reboot_required(void);
|
||||
|
||||
#else
|
||||
|
|
|
@ -535,6 +535,15 @@ static void native_machine_emergency_restart(void)
|
|||
mode = reboot_mode == REBOOT_WARM ? 0x1234 : 0;
|
||||
*((unsigned short *)__va(0x472)) = mode;
|
||||
|
||||
/*
|
||||
* If an EFI capsule has been registered with the firmware then
|
||||
* override the reboot= parameter.
|
||||
*/
|
||||
if (efi_capsule_pending(NULL)) {
|
||||
pr_info("EFI capsule is pending, forcing EFI reboot.\n");
|
||||
reboot_type = BOOT_EFI;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
/* Could also try the reset bit in the Hammer NB */
|
||||
switch (reboot_type) {
|
||||
|
|
|
@ -68,6 +68,21 @@ struct efifb_dmi_info efifb_dmi_list[] = {
|
|||
[M_UNKNOWN] = { NULL, 0, 0, 0, 0, OVERRIDE_NONE }
|
||||
};
|
||||
|
||||
void efifb_setup_from_dmi(struct screen_info *si, const char *opt)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < M_UNKNOWN; i++) {
|
||||
if (efifb_dmi_list[i].base != 0 &&
|
||||
!strcmp(opt, efifb_dmi_list[i].optname)) {
|
||||
si->lfb_base = efifb_dmi_list[i].base;
|
||||
si->lfb_linelength = efifb_dmi_list[i].stride;
|
||||
si->lfb_width = efifb_dmi_list[i].width;
|
||||
si->lfb_height = efifb_dmi_list[i].height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define choose_value(dmivalue, fwvalue, field, flags) ({ \
|
||||
typeof(fwvalue) _ret_ = fwvalue; \
|
||||
if ((flags) & (field)) \
|
||||
|
|
|
@ -1125,8 +1125,14 @@ static int populate_pgd(struct cpa_data *cpa, unsigned long addr)
|
|||
static int __cpa_process_fault(struct cpa_data *cpa, unsigned long vaddr,
|
||||
int primary)
|
||||
{
|
||||
if (cpa->pgd)
|
||||
if (cpa->pgd) {
|
||||
/*
|
||||
* Right now, we only execute this code path when mapping
|
||||
* the EFI virtual memory map regions, no other users
|
||||
* provide a ->pgd value. This may change in the future.
|
||||
*/
|
||||
return populate_pgd(cpa, vaddr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ignore all non primary paths.
|
||||
|
|
|
@ -54,10 +54,6 @@
|
|||
#include <asm/rtc.h>
|
||||
#include <asm/uv/uv.h>
|
||||
|
||||
#define EFI_DEBUG
|
||||
|
||||
struct efi_memory_map memmap;
|
||||
|
||||
static struct efi efi_phys __initdata;
|
||||
static efi_system_table_t efi_systab __initdata;
|
||||
|
||||
|
@ -119,11 +115,10 @@ void efi_get_time(struct timespec *now)
|
|||
|
||||
void __init efi_find_mirror(void)
|
||||
{
|
||||
void *p;
|
||||
efi_memory_desc_t *md;
|
||||
u64 mirror_size = 0, total_size = 0;
|
||||
|
||||
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
|
||||
efi_memory_desc_t *md = p;
|
||||
for_each_efi_memory_desc(md) {
|
||||
unsigned long long start = md->phys_addr;
|
||||
unsigned long long size = md->num_pages << EFI_PAGE_SHIFT;
|
||||
|
||||
|
@ -146,10 +141,9 @@ void __init efi_find_mirror(void)
|
|||
|
||||
static void __init do_add_efi_memmap(void)
|
||||
{
|
||||
void *p;
|
||||
efi_memory_desc_t *md;
|
||||
|
||||
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
|
||||
efi_memory_desc_t *md = p;
|
||||
for_each_efi_memory_desc(md) {
|
||||
unsigned long long start = md->phys_addr;
|
||||
unsigned long long size = md->num_pages << EFI_PAGE_SHIFT;
|
||||
int e820_type;
|
||||
|
@ -209,47 +203,47 @@ int __init efi_memblock_x86_reserve_range(void)
|
|||
#else
|
||||
pmap = (e->efi_memmap | ((__u64)e->efi_memmap_hi << 32));
|
||||
#endif
|
||||
memmap.phys_map = pmap;
|
||||
memmap.nr_map = e->efi_memmap_size /
|
||||
efi.memmap.phys_map = pmap;
|
||||
efi.memmap.nr_map = e->efi_memmap_size /
|
||||
e->efi_memdesc_size;
|
||||
memmap.desc_size = e->efi_memdesc_size;
|
||||
memmap.desc_version = e->efi_memdesc_version;
|
||||
efi.memmap.desc_size = e->efi_memdesc_size;
|
||||
efi.memmap.desc_version = e->efi_memdesc_version;
|
||||
|
||||
memblock_reserve(pmap, memmap.nr_map * memmap.desc_size);
|
||||
WARN(efi.memmap.desc_version != 1,
|
||||
"Unexpected EFI_MEMORY_DESCRIPTOR version %ld",
|
||||
efi.memmap.desc_version);
|
||||
|
||||
efi.memmap = &memmap;
|
||||
memblock_reserve(pmap, efi.memmap.nr_map * efi.memmap.desc_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __init efi_print_memmap(void)
|
||||
{
|
||||
#ifdef EFI_DEBUG
|
||||
efi_memory_desc_t *md;
|
||||
void *p;
|
||||
int i;
|
||||
int i = 0;
|
||||
|
||||
for (p = memmap.map, i = 0;
|
||||
p < memmap.map_end;
|
||||
p += memmap.desc_size, i++) {
|
||||
for_each_efi_memory_desc(md) {
|
||||
char buf[64];
|
||||
|
||||
md = p;
|
||||
pr_info("mem%02u: %s range=[0x%016llx-0x%016llx] (%lluMB)\n",
|
||||
i, efi_md_typeattr_format(buf, sizeof(buf), md),
|
||||
i++, efi_md_typeattr_format(buf, sizeof(buf), md),
|
||||
md->phys_addr,
|
||||
md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1,
|
||||
(md->num_pages >> (20 - EFI_PAGE_SHIFT)));
|
||||
}
|
||||
#endif /* EFI_DEBUG */
|
||||
}
|
||||
|
||||
void __init efi_unmap_memmap(void)
|
||||
{
|
||||
unsigned long size;
|
||||
|
||||
clear_bit(EFI_MEMMAP, &efi.flags);
|
||||
if (memmap.map) {
|
||||
early_memunmap(memmap.map, memmap.nr_map * memmap.desc_size);
|
||||
memmap.map = NULL;
|
||||
|
||||
size = efi.memmap.nr_map * efi.memmap.desc_size;
|
||||
if (efi.memmap.map) {
|
||||
early_memunmap(efi.memmap.map, size);
|
||||
efi.memmap.map = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -352,8 +346,6 @@ static int __init efi_systab_init(void *phys)
|
|||
efi.systab->hdr.revision >> 16,
|
||||
efi.systab->hdr.revision & 0xffff);
|
||||
|
||||
set_bit(EFI_SYSTEM_TABLES, &efi.flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -440,17 +432,22 @@ static int __init efi_runtime_init(void)
|
|||
|
||||
static int __init efi_memmap_init(void)
|
||||
{
|
||||
unsigned long addr, size;
|
||||
|
||||
if (efi_enabled(EFI_PARAVIRT))
|
||||
return 0;
|
||||
|
||||
/* Map the EFI memory map */
|
||||
memmap.map = early_memremap((unsigned long)memmap.phys_map,
|
||||
memmap.nr_map * memmap.desc_size);
|
||||
if (memmap.map == NULL) {
|
||||
size = efi.memmap.nr_map * efi.memmap.desc_size;
|
||||
addr = (unsigned long)efi.memmap.phys_map;
|
||||
|
||||
efi.memmap.map = early_memremap(addr, size);
|
||||
if (efi.memmap.map == NULL) {
|
||||
pr_err("Could not map the memory map!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memmap.map_end = memmap.map + (memmap.nr_map * memmap.desc_size);
|
||||
|
||||
efi.memmap.map_end = efi.memmap.map + size;
|
||||
|
||||
if (add_efi_memmap)
|
||||
do_add_efi_memmap();
|
||||
|
@ -552,12 +549,9 @@ void __init efi_set_executable(efi_memory_desc_t *md, bool executable)
|
|||
void __init runtime_code_page_mkexec(void)
|
||||
{
|
||||
efi_memory_desc_t *md;
|
||||
void *p;
|
||||
|
||||
/* Make EFI runtime service code area executable */
|
||||
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
|
||||
md = p;
|
||||
|
||||
for_each_efi_memory_desc(md) {
|
||||
if (md->type != EFI_RUNTIME_SERVICES_CODE)
|
||||
continue;
|
||||
|
||||
|
@ -604,12 +598,10 @@ void __init old_map_region(efi_memory_desc_t *md)
|
|||
/* Merge contiguous regions of the same type and attribute */
|
||||
static void __init efi_merge_regions(void)
|
||||
{
|
||||
void *p;
|
||||
efi_memory_desc_t *md, *prev_md = NULL;
|
||||
|
||||
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
|
||||
for_each_efi_memory_desc(md) {
|
||||
u64 prev_size;
|
||||
md = p;
|
||||
|
||||
if (!prev_md) {
|
||||
prev_md = md;
|
||||
|
@ -651,30 +643,31 @@ static void __init get_systab_virt_addr(efi_memory_desc_t *md)
|
|||
static void __init save_runtime_map(void)
|
||||
{
|
||||
#ifdef CONFIG_KEXEC_CORE
|
||||
unsigned long desc_size;
|
||||
efi_memory_desc_t *md;
|
||||
void *tmp, *p, *q = NULL;
|
||||
void *tmp, *q = NULL;
|
||||
int count = 0;
|
||||
|
||||
if (efi_enabled(EFI_OLD_MEMMAP))
|
||||
return;
|
||||
|
||||
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
|
||||
md = p;
|
||||
desc_size = efi.memmap.desc_size;
|
||||
|
||||
for_each_efi_memory_desc(md) {
|
||||
if (!(md->attribute & EFI_MEMORY_RUNTIME) ||
|
||||
(md->type == EFI_BOOT_SERVICES_CODE) ||
|
||||
(md->type == EFI_BOOT_SERVICES_DATA))
|
||||
continue;
|
||||
tmp = krealloc(q, (count + 1) * memmap.desc_size, GFP_KERNEL);
|
||||
tmp = krealloc(q, (count + 1) * desc_size, GFP_KERNEL);
|
||||
if (!tmp)
|
||||
goto out;
|
||||
q = tmp;
|
||||
|
||||
memcpy(q + count * memmap.desc_size, md, memmap.desc_size);
|
||||
memcpy(q + count * desc_size, md, desc_size);
|
||||
count++;
|
||||
}
|
||||
|
||||
efi_runtime_map_setup(q, count, memmap.desc_size);
|
||||
efi_runtime_map_setup(q, count, desc_size);
|
||||
return;
|
||||
|
||||
out:
|
||||
|
@ -714,10 +707,10 @@ static inline void *efi_map_next_entry_reverse(void *entry)
|
|||
{
|
||||
/* Initial call */
|
||||
if (!entry)
|
||||
return memmap.map_end - memmap.desc_size;
|
||||
return efi.memmap.map_end - efi.memmap.desc_size;
|
||||
|
||||
entry -= memmap.desc_size;
|
||||
if (entry < memmap.map)
|
||||
entry -= efi.memmap.desc_size;
|
||||
if (entry < efi.memmap.map)
|
||||
return NULL;
|
||||
|
||||
return entry;
|
||||
|
@ -759,10 +752,10 @@ static void *efi_map_next_entry(void *entry)
|
|||
|
||||
/* Initial call */
|
||||
if (!entry)
|
||||
return memmap.map;
|
||||
return efi.memmap.map;
|
||||
|
||||
entry += memmap.desc_size;
|
||||
if (entry >= memmap.map_end)
|
||||
entry += efi.memmap.desc_size;
|
||||
if (entry >= efi.memmap.map_end)
|
||||
return NULL;
|
||||
|
||||
return entry;
|
||||
|
@ -776,8 +769,11 @@ static void * __init efi_map_regions(int *count, int *pg_shift)
|
|||
{
|
||||
void *p, *new_memmap = NULL;
|
||||
unsigned long left = 0;
|
||||
unsigned long desc_size;
|
||||
efi_memory_desc_t *md;
|
||||
|
||||
desc_size = efi.memmap.desc_size;
|
||||
|
||||
p = NULL;
|
||||
while ((p = efi_map_next_entry(p))) {
|
||||
md = p;
|
||||
|
@ -792,7 +788,7 @@ static void * __init efi_map_regions(int *count, int *pg_shift)
|
|||
efi_map_region(md);
|
||||
get_systab_virt_addr(md);
|
||||
|
||||
if (left < memmap.desc_size) {
|
||||
if (left < desc_size) {
|
||||
new_memmap = realloc_pages(new_memmap, *pg_shift);
|
||||
if (!new_memmap)
|
||||
return NULL;
|
||||
|
@ -801,10 +797,9 @@ static void * __init efi_map_regions(int *count, int *pg_shift)
|
|||
(*pg_shift)++;
|
||||
}
|
||||
|
||||
memcpy(new_memmap + (*count * memmap.desc_size), md,
|
||||
memmap.desc_size);
|
||||
memcpy(new_memmap + (*count * desc_size), md, desc_size);
|
||||
|
||||
left -= memmap.desc_size;
|
||||
left -= desc_size;
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
|
@ -816,7 +811,6 @@ static void __init kexec_enter_virtual_mode(void)
|
|||
#ifdef CONFIG_KEXEC_CORE
|
||||
efi_memory_desc_t *md;
|
||||
unsigned int num_pages;
|
||||
void *p;
|
||||
|
||||
efi.systab = NULL;
|
||||
|
||||
|
@ -840,8 +834,7 @@ static void __init kexec_enter_virtual_mode(void)
|
|||
* Map efi regions which were passed via setup_data. The virt_addr is a
|
||||
* fixed addr which was used in first kernel of a kexec boot.
|
||||
*/
|
||||
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
|
||||
md = p;
|
||||
for_each_efi_memory_desc(md) {
|
||||
efi_map_region_fixed(md); /* FIXME: add error handling */
|
||||
get_systab_virt_addr(md);
|
||||
}
|
||||
|
@ -850,10 +843,10 @@ static void __init kexec_enter_virtual_mode(void)
|
|||
|
||||
BUG_ON(!efi.systab);
|
||||
|
||||
num_pages = ALIGN(memmap.nr_map * memmap.desc_size, PAGE_SIZE);
|
||||
num_pages = ALIGN(efi.memmap.nr_map * efi.memmap.desc_size, PAGE_SIZE);
|
||||
num_pages >>= PAGE_SHIFT;
|
||||
|
||||
if (efi_setup_page_tables(memmap.phys_map, num_pages)) {
|
||||
if (efi_setup_page_tables(efi.memmap.phys_map, num_pages)) {
|
||||
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
|
||||
return;
|
||||
}
|
||||
|
@ -937,16 +930,16 @@ static void __init __efi_enter_virtual_mode(void)
|
|||
|
||||
if (efi_is_native()) {
|
||||
status = phys_efi_set_virtual_address_map(
|
||||
memmap.desc_size * count,
|
||||
memmap.desc_size,
|
||||
memmap.desc_version,
|
||||
efi.memmap.desc_size * count,
|
||||
efi.memmap.desc_size,
|
||||
efi.memmap.desc_version,
|
||||
(efi_memory_desc_t *)__pa(new_memmap));
|
||||
} else {
|
||||
status = efi_thunk_set_virtual_address_map(
|
||||
efi_phys.set_virtual_address_map,
|
||||
memmap.desc_size * count,
|
||||
memmap.desc_size,
|
||||
memmap.desc_version,
|
||||
efi.memmap.desc_size * count,
|
||||
efi.memmap.desc_size,
|
||||
efi.memmap.desc_version,
|
||||
(efi_memory_desc_t *)__pa(new_memmap));
|
||||
}
|
||||
|
||||
|
@ -1011,13 +1004,11 @@ void __init efi_enter_virtual_mode(void)
|
|||
u32 efi_mem_type(unsigned long phys_addr)
|
||||
{
|
||||
efi_memory_desc_t *md;
|
||||
void *p;
|
||||
|
||||
if (!efi_enabled(EFI_MEMMAP))
|
||||
return 0;
|
||||
|
||||
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
|
||||
md = p;
|
||||
for_each_efi_memory_desc(md) {
|
||||
if ((md->phys_addr <= phys_addr) &&
|
||||
(phys_addr < (md->phys_addr +
|
||||
(md->num_pages << EFI_PAGE_SHIFT))))
|
||||
|
|
|
@ -55,14 +55,12 @@ struct efi_scratch efi_scratch;
|
|||
static void __init early_code_mapping_set_exec(int executable)
|
||||
{
|
||||
efi_memory_desc_t *md;
|
||||
void *p;
|
||||
|
||||
if (!(__supported_pte_mask & _PAGE_NX))
|
||||
return;
|
||||
|
||||
/* Make EFI service code area executable */
|
||||
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
|
||||
md = p;
|
||||
for_each_efi_memory_desc(md) {
|
||||
if (md->type == EFI_RUNTIME_SERVICES_CODE ||
|
||||
md->type == EFI_BOOT_SERVICES_CODE)
|
||||
efi_set_executable(md, executable);
|
||||
|
@ -253,7 +251,7 @@ int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
|
|||
* Map all of RAM so that we can access arguments in the 1:1
|
||||
* mapping when making EFI runtime calls.
|
||||
*/
|
||||
for_each_efi_memory_desc(&memmap, md) {
|
||||
for_each_efi_memory_desc(md) {
|
||||
if (md->type != EFI_CONVENTIONAL_MEMORY &&
|
||||
md->type != EFI_LOADER_DATA &&
|
||||
md->type != EFI_LOADER_CODE)
|
||||
|
@ -398,7 +396,6 @@ void __init efi_runtime_update_mappings(void)
|
|||
unsigned long pfn;
|
||||
pgd_t *pgd = efi_pgd;
|
||||
efi_memory_desc_t *md;
|
||||
void *p;
|
||||
|
||||
if (efi_enabled(EFI_OLD_MEMMAP)) {
|
||||
if (__supported_pte_mask & _PAGE_NX)
|
||||
|
@ -409,9 +406,8 @@ void __init efi_runtime_update_mappings(void)
|
|||
if (!efi_enabled(EFI_NX_PE_DATA))
|
||||
return;
|
||||
|
||||
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
|
||||
for_each_efi_memory_desc(md) {
|
||||
unsigned long pf = 0;
|
||||
md = p;
|
||||
|
||||
if (!(md->attribute & EFI_MEMORY_RUNTIME))
|
||||
continue;
|
||||
|
|
|
@ -195,10 +195,9 @@ static bool can_free_region(u64 start, u64 size)
|
|||
*/
|
||||
void __init efi_reserve_boot_services(void)
|
||||
{
|
||||
void *p;
|
||||
efi_memory_desc_t *md;
|
||||
|
||||
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
|
||||
efi_memory_desc_t *md = p;
|
||||
for_each_efi_memory_desc(md) {
|
||||
u64 start = md->phys_addr;
|
||||
u64 size = md->num_pages << EFI_PAGE_SHIFT;
|
||||
bool already_reserved;
|
||||
|
@ -250,10 +249,9 @@ void __init efi_reserve_boot_services(void)
|
|||
|
||||
void __init efi_free_boot_services(void)
|
||||
{
|
||||
void *p;
|
||||
efi_memory_desc_t *md;
|
||||
|
||||
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
|
||||
efi_memory_desc_t *md = p;
|
||||
for_each_efi_memory_desc(md) {
|
||||
unsigned long long start = md->phys_addr;
|
||||
unsigned long long size = md->num_pages << EFI_PAGE_SHIFT;
|
||||
|
||||
|
|
|
@ -87,6 +87,31 @@ config EFI_RUNTIME_WRAPPERS
|
|||
config EFI_ARMSTUB
|
||||
bool
|
||||
|
||||
config EFI_BOOTLOADER_CONTROL
|
||||
tristate "EFI Bootloader Control"
|
||||
depends on EFI_VARS
|
||||
default n
|
||||
---help---
|
||||
This module installs a reboot hook, such that if reboot() is
|
||||
invoked with a string argument NNN, "NNN" is copied to the
|
||||
"LoaderEntryOneShot" EFI variable, to be read by the
|
||||
bootloader. If the string matches one of the boot labels
|
||||
defined in its configuration, the bootloader will boot once
|
||||
to that label. The "LoaderEntryRebootReason" EFI variable is
|
||||
set with the reboot reason: "reboot" or "shutdown". The
|
||||
bootloader reads this reboot reason and takes particular
|
||||
action according to its policy.
|
||||
|
||||
config EFI_CAPSULE_LOADER
|
||||
tristate "EFI capsule loader"
|
||||
depends on EFI
|
||||
help
|
||||
This option exposes a loader interface "/dev/efi_capsule_loader" for
|
||||
users to load EFI capsules. This driver requires working runtime
|
||||
capsule support in the firmware, which many OEMs do not provide.
|
||||
|
||||
Most users should say N.
|
||||
|
||||
endmenu
|
||||
|
||||
config UEFI_CPER
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
#
|
||||
KASAN_SANITIZE_runtime-wrappers.o := n
|
||||
|
||||
obj-$(CONFIG_EFI) += efi.o vars.o reboot.o
|
||||
obj-$(CONFIG_EFI) += efi.o vars.o reboot.o memattr.o
|
||||
obj-$(CONFIG_EFI) += capsule.o
|
||||
obj-$(CONFIG_EFI_VARS) += efivars.o
|
||||
obj-$(CONFIG_EFI_ESRT) += esrt.o
|
||||
obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o
|
||||
|
@ -18,7 +19,9 @@ obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o
|
|||
obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o
|
||||
obj-$(CONFIG_EFI_STUB) += libstub/
|
||||
obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_mem.o
|
||||
obj-$(CONFIG_EFI_BOOTLOADER_CONTROL) += efibc.o
|
||||
|
||||
arm-obj-$(CONFIG_EFI) := arm-init.o arm-runtime.o
|
||||
obj-$(CONFIG_ARM) += $(arm-obj-y)
|
||||
obj-$(CONFIG_ARM64) += $(arm-obj-y)
|
||||
obj-$(CONFIG_EFI_CAPSULE_LOADER) += capsule-loader.o
|
||||
|
|
|
@ -11,17 +11,19 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "efi: " fmt
|
||||
|
||||
#include <linux/efi.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/mm_types.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_fdt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/screen_info.h>
|
||||
|
||||
#include <asm/efi.h>
|
||||
|
||||
struct efi_memory_map memmap;
|
||||
|
||||
u64 efi_system_table;
|
||||
|
||||
static int __init is_normal_ram(efi_memory_desc_t *md)
|
||||
|
@ -40,7 +42,7 @@ static phys_addr_t efi_to_phys(unsigned long addr)
|
|||
{
|
||||
efi_memory_desc_t *md;
|
||||
|
||||
for_each_efi_memory_desc(&memmap, md) {
|
||||
for_each_efi_memory_desc(md) {
|
||||
if (!(md->attribute & EFI_MEMORY_RUNTIME))
|
||||
continue;
|
||||
if (md->virt_addr == 0)
|
||||
|
@ -53,6 +55,36 @@ static phys_addr_t efi_to_phys(unsigned long addr)
|
|||
return addr;
|
||||
}
|
||||
|
||||
static __initdata unsigned long screen_info_table = EFI_INVALID_TABLE_ADDR;
|
||||
|
||||
static __initdata efi_config_table_type_t arch_tables[] = {
|
||||
{LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, NULL, &screen_info_table},
|
||||
{NULL_GUID, NULL, NULL}
|
||||
};
|
||||
|
||||
static void __init init_screen_info(void)
|
||||
{
|
||||
struct screen_info *si;
|
||||
|
||||
if (screen_info_table != EFI_INVALID_TABLE_ADDR) {
|
||||
si = early_memremap_ro(screen_info_table, sizeof(*si));
|
||||
if (!si) {
|
||||
pr_err("Could not map screen_info config table\n");
|
||||
return;
|
||||
}
|
||||
screen_info = *si;
|
||||
early_memunmap(si, sizeof(*si));
|
||||
|
||||
/* dummycon on ARM needs non-zero values for columns/lines */
|
||||
screen_info.orig_video_cols = 80;
|
||||
screen_info.orig_video_lines = 25;
|
||||
}
|
||||
|
||||
if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI &&
|
||||
memblock_is_map_memory(screen_info.lfb_base))
|
||||
memblock_mark_nomap(screen_info.lfb_base, screen_info.lfb_size);
|
||||
}
|
||||
|
||||
static int __init uefi_init(void)
|
||||
{
|
||||
efi_char16_t *c16;
|
||||
|
@ -85,6 +117,8 @@ static int __init uefi_init(void)
|
|||
efi.systab->hdr.revision >> 16,
|
||||
efi.systab->hdr.revision & 0xffff);
|
||||
|
||||
efi.runtime_version = efi.systab->hdr.revision;
|
||||
|
||||
/* Show what we know for posterity */
|
||||
c16 = early_memremap_ro(efi_to_phys(efi.systab->fw_vendor),
|
||||
sizeof(vendor) * sizeof(efi_char16_t));
|
||||
|
@ -108,7 +142,8 @@ static int __init uefi_init(void)
|
|||
goto out;
|
||||
}
|
||||
retval = efi_config_parse_tables(config_tables, efi.systab->nr_tables,
|
||||
sizeof(efi_config_table_t), NULL);
|
||||
sizeof(efi_config_table_t),
|
||||
arch_tables);
|
||||
|
||||
early_memunmap(config_tables, table_size);
|
||||
out:
|
||||
|
@ -143,7 +178,7 @@ static __init void reserve_regions(void)
|
|||
if (efi_enabled(EFI_DBG))
|
||||
pr_info("Processing EFI memory map:\n");
|
||||
|
||||
for_each_efi_memory_desc(&memmap, md) {
|
||||
for_each_efi_memory_desc(md) {
|
||||
paddr = md->phys_addr;
|
||||
npages = md->num_pages;
|
||||
|
||||
|
@ -184,9 +219,9 @@ void __init efi_init(void)
|
|||
|
||||
efi_system_table = params.system_table;
|
||||
|
||||
memmap.phys_map = params.mmap;
|
||||
memmap.map = early_memremap_ro(params.mmap, params.mmap_size);
|
||||
if (memmap.map == NULL) {
|
||||
efi.memmap.phys_map = params.mmap;
|
||||
efi.memmap.map = early_memremap_ro(params.mmap, params.mmap_size);
|
||||
if (efi.memmap.map == NULL) {
|
||||
/*
|
||||
* If we are booting via UEFI, the UEFI memory map is the only
|
||||
* description of memory we have, so there is little point in
|
||||
|
@ -194,28 +229,37 @@ void __init efi_init(void)
|
|||
*/
|
||||
panic("Unable to map EFI memory map.\n");
|
||||
}
|
||||
memmap.map_end = memmap.map + params.mmap_size;
|
||||
memmap.desc_size = params.desc_size;
|
||||
memmap.desc_version = params.desc_ver;
|
||||
efi.memmap.map_end = efi.memmap.map + params.mmap_size;
|
||||
efi.memmap.desc_size = params.desc_size;
|
||||
efi.memmap.desc_version = params.desc_ver;
|
||||
|
||||
WARN(efi.memmap.desc_version != 1,
|
||||
"Unexpected EFI_MEMORY_DESCRIPTOR version %ld",
|
||||
efi.memmap.desc_version);
|
||||
|
||||
if (uefi_init() < 0)
|
||||
return;
|
||||
|
||||
reserve_regions();
|
||||
early_memunmap(memmap.map, params.mmap_size);
|
||||
efi_memattr_init();
|
||||
early_memunmap(efi.memmap.map, params.mmap_size);
|
||||
|
||||
if (IS_ENABLED(CONFIG_ARM)) {
|
||||
/*
|
||||
* ARM currently does not allow ioremap_cache() to be called on
|
||||
* memory regions that are covered by struct page. So remove the
|
||||
* UEFI memory map from the linear mapping.
|
||||
*/
|
||||
memblock_mark_nomap(params.mmap & PAGE_MASK,
|
||||
PAGE_ALIGN(params.mmap_size +
|
||||
(params.mmap & ~PAGE_MASK)));
|
||||
} else {
|
||||
memblock_reserve(params.mmap & PAGE_MASK,
|
||||
PAGE_ALIGN(params.mmap_size +
|
||||
(params.mmap & ~PAGE_MASK)));
|
||||
}
|
||||
|
||||
init_screen_info();
|
||||
}
|
||||
|
||||
static int __init register_gop_device(void)
|
||||
{
|
||||
void *pd;
|
||||
|
||||
if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
|
||||
return 0;
|
||||
|
||||
pd = platform_device_register_data(NULL, "efi-framebuffer", 0,
|
||||
&screen_info, sizeof(screen_info));
|
||||
return PTR_ERR_OR_ZERO(pd);
|
||||
}
|
||||
subsys_initcall(register_gop_device);
|
||||
|
|
|
@ -42,11 +42,13 @@ static struct mm_struct efi_mm = {
|
|||
static bool __init efi_virtmap_init(void)
|
||||
{
|
||||
efi_memory_desc_t *md;
|
||||
bool systab_found;
|
||||
|
||||
efi_mm.pgd = pgd_alloc(&efi_mm);
|
||||
init_new_context(NULL, &efi_mm);
|
||||
|
||||
for_each_efi_memory_desc(&memmap, md) {
|
||||
systab_found = false;
|
||||
for_each_efi_memory_desc(md) {
|
||||
phys_addr_t phys = md->phys_addr;
|
||||
int ret;
|
||||
|
||||
|
@ -64,7 +66,25 @@ static bool __init efi_virtmap_init(void)
|
|||
&phys, ret);
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* If this entry covers the address of the UEFI system table,
|
||||
* calculate and record its virtual address.
|
||||
*/
|
||||
if (efi_system_table >= phys &&
|
||||
efi_system_table < phys + (md->num_pages * EFI_PAGE_SIZE)) {
|
||||
efi.systab = (void *)(unsigned long)(efi_system_table -
|
||||
phys + md->virt_addr);
|
||||
systab_found = true;
|
||||
}
|
||||
}
|
||||
if (!systab_found) {
|
||||
pr_err("No virtual mapping found for the UEFI System Table\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (efi_memattr_apply_permissions(&efi_mm, efi_set_mapping_permissions))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -89,26 +109,17 @@ static int __init arm_enable_runtime_services(void)
|
|||
|
||||
pr_info("Remapping and enabling EFI services.\n");
|
||||
|
||||
mapsize = memmap.map_end - memmap.map;
|
||||
memmap.map = (__force void *)ioremap_cache(memmap.phys_map,
|
||||
mapsize);
|
||||
if (!memmap.map) {
|
||||
mapsize = efi.memmap.map_end - efi.memmap.map;
|
||||
|
||||
efi.memmap.map = memremap(efi.memmap.phys_map, mapsize, MEMREMAP_WB);
|
||||
if (!efi.memmap.map) {
|
||||
pr_err("Failed to remap EFI memory map\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memmap.map_end = 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);
|
||||
efi.memmap.map_end = efi.memmap.map + mapsize;
|
||||
|
||||
if (!efi_virtmap_init()) {
|
||||
pr_err("No UEFI virtual mapping was installed -- runtime services will not be available\n");
|
||||
pr_err("UEFI virtual mapping missing or invalid -- runtime services will not be available\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
@ -116,8 +127,6 @@ static int __init arm_enable_runtime_services(void)
|
|||
efi_native_runtime_setup();
|
||||
set_bit(EFI_RUNTIME_SERVICES, &efi.flags);
|
||||
|
||||
efi.runtime_version = efi.systab->hdr.revision;
|
||||
|
||||
return 0;
|
||||
}
|
||||
early_initcall(arm_enable_runtime_services);
|
||||
|
|
|
@ -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");
|
|
@ -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);
|
|
@ -43,6 +43,7 @@ struct efi __read_mostly efi = {
|
|||
.config_table = EFI_INVALID_TABLE_ADDR,
|
||||
.esrt = EFI_INVALID_TABLE_ADDR,
|
||||
.properties_table = EFI_INVALID_TABLE_ADDR,
|
||||
.mem_attr_table = EFI_INVALID_TABLE_ADDR,
|
||||
};
|
||||
EXPORT_SYMBOL(efi);
|
||||
|
||||
|
@ -256,7 +257,7 @@ subsys_initcall(efisubsys_init);
|
|||
*/
|
||||
int __init efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md)
|
||||
{
|
||||
struct efi_memory_map *map = efi.memmap;
|
||||
struct efi_memory_map *map = &efi.memmap;
|
||||
phys_addr_t p, e;
|
||||
|
||||
if (!efi_enabled(EFI_MEMMAP)) {
|
||||
|
@ -338,6 +339,7 @@ static __initdata efi_config_table_type_t common_tables[] = {
|
|||
{UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga},
|
||||
{EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt},
|
||||
{EFI_PROPERTIES_TABLE_GUID, "PROP", &efi.properties_table},
|
||||
{EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi.mem_attr_table},
|
||||
{NULL_GUID, NULL, NULL},
|
||||
};
|
||||
|
||||
|
@ -351,6 +353,7 @@ static __init int match_config_table(efi_guid_t *guid,
|
|||
for (i = 0; efi_guidcmp(table_types[i].guid, NULL_GUID); i++) {
|
||||
if (!efi_guidcmp(*guid, table_types[i].guid)) {
|
||||
*(table_types[i].ptr) = table;
|
||||
if (table_types[i].name)
|
||||
pr_cont(" %s=0x%lx ",
|
||||
table_types[i].name, table);
|
||||
return 1;
|
||||
|
@ -620,16 +623,12 @@ char * __init efi_md_typeattr_format(char *buf, size_t size,
|
|||
*/
|
||||
u64 __weak efi_mem_attributes(unsigned long phys_addr)
|
||||
{
|
||||
struct efi_memory_map *map;
|
||||
efi_memory_desc_t *md;
|
||||
void *p;
|
||||
|
||||
if (!efi_enabled(EFI_MEMMAP))
|
||||
return 0;
|
||||
|
||||
map = efi.memmap;
|
||||
for (p = map->map; p < map->map_end; p += map->desc_size) {
|
||||
md = p;
|
||||
for_each_efi_memory_desc(md) {
|
||||
if ((md->phys_addr <= phys_addr) &&
|
||||
(phys_addr < (md->phys_addr +
|
||||
(md->num_pages << EFI_PAGE_SHIFT))))
|
||||
|
@ -637,3 +636,36 @@ u64 __weak efi_mem_attributes(unsigned long phys_addr)
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int efi_status_to_err(efi_status_t status)
|
||||
{
|
||||
int err;
|
||||
|
||||
switch (status) {
|
||||
case EFI_SUCCESS:
|
||||
err = 0;
|
||||
break;
|
||||
case EFI_INVALID_PARAMETER:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
case EFI_OUT_OF_RESOURCES:
|
||||
err = -ENOSPC;
|
||||
break;
|
||||
case EFI_DEVICE_ERROR:
|
||||
err = -EIO;
|
||||
break;
|
||||
case EFI_WRITE_PROTECTED:
|
||||
err = -EROFS;
|
||||
break;
|
||||
case EFI_SECURITY_VIOLATION:
|
||||
err = -EACCES;
|
||||
break;
|
||||
case EFI_NOT_FOUND:
|
||||
err = -ENOENT;
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -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");
|
|
@ -661,7 +661,7 @@ static void efivar_update_sysfs_entries(struct work_struct *work)
|
|||
return;
|
||||
|
||||
err = efivar_init(efivar_update_sysfs_entry, entry,
|
||||
true, false, &efivar_sysfs_list);
|
||||
false, &efivar_sysfs_list);
|
||||
if (!err)
|
||||
break;
|
||||
|
||||
|
@ -730,8 +730,7 @@ int efivars_sysfs_init(void)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
efivar_init(efivars_sysfs_callback, NULL, false,
|
||||
true, &efivar_sysfs_list);
|
||||
efivar_init(efivars_sysfs_callback, NULL, true, &efivar_sysfs_list);
|
||||
|
||||
error = create_efivars_bin_attributes();
|
||||
if (error) {
|
||||
|
|
|
@ -57,7 +57,7 @@ static int __init cmp_fake_mem(const void *x1, const void *x2)
|
|||
void __init efi_fake_memmap(void)
|
||||
{
|
||||
u64 start, end, m_start, m_end, m_attr;
|
||||
int new_nr_map = memmap.nr_map;
|
||||
int new_nr_map = efi.memmap.nr_map;
|
||||
efi_memory_desc_t *md;
|
||||
phys_addr_t new_memmap_phy;
|
||||
void *new_memmap;
|
||||
|
@ -68,8 +68,7 @@ void __init efi_fake_memmap(void)
|
|||
return;
|
||||
|
||||
/* count up the number of EFI memory descriptor */
|
||||
for (old = memmap.map; old < memmap.map_end; old += memmap.desc_size) {
|
||||
md = old;
|
||||
for_each_efi_memory_desc(md) {
|
||||
start = md->phys_addr;
|
||||
end = start + (md->num_pages << EFI_PAGE_SHIFT) - 1;
|
||||
|
||||
|
@ -95,25 +94,25 @@ void __init efi_fake_memmap(void)
|
|||
}
|
||||
|
||||
/* allocate memory for new EFI memmap */
|
||||
new_memmap_phy = memblock_alloc(memmap.desc_size * new_nr_map,
|
||||
new_memmap_phy = memblock_alloc(efi.memmap.desc_size * new_nr_map,
|
||||
PAGE_SIZE);
|
||||
if (!new_memmap_phy)
|
||||
return;
|
||||
|
||||
/* create new EFI memmap */
|
||||
new_memmap = early_memremap(new_memmap_phy,
|
||||
memmap.desc_size * new_nr_map);
|
||||
efi.memmap.desc_size * new_nr_map);
|
||||
if (!new_memmap) {
|
||||
memblock_free(new_memmap_phy, memmap.desc_size * new_nr_map);
|
||||
memblock_free(new_memmap_phy, efi.memmap.desc_size * new_nr_map);
|
||||
return;
|
||||
}
|
||||
|
||||
for (old = memmap.map, new = new_memmap;
|
||||
old < memmap.map_end;
|
||||
old += memmap.desc_size, new += memmap.desc_size) {
|
||||
for (old = efi.memmap.map, new = new_memmap;
|
||||
old < efi.memmap.map_end;
|
||||
old += efi.memmap.desc_size, new += efi.memmap.desc_size) {
|
||||
|
||||
/* copy original EFI memory descriptor */
|
||||
memcpy(new, old, memmap.desc_size);
|
||||
memcpy(new, old, efi.memmap.desc_size);
|
||||
md = new;
|
||||
start = md->phys_addr;
|
||||
end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1;
|
||||
|
@ -134,8 +133,8 @@ void __init efi_fake_memmap(void)
|
|||
md->num_pages = (m_end - md->phys_addr + 1) >>
|
||||
EFI_PAGE_SHIFT;
|
||||
/* latter part */
|
||||
new += memmap.desc_size;
|
||||
memcpy(new, old, memmap.desc_size);
|
||||
new += efi.memmap.desc_size;
|
||||
memcpy(new, old, efi.memmap.desc_size);
|
||||
md = new;
|
||||
md->phys_addr = m_end + 1;
|
||||
md->num_pages = (end - md->phys_addr + 1) >>
|
||||
|
@ -147,16 +146,16 @@ void __init efi_fake_memmap(void)
|
|||
md->num_pages = (m_start - md->phys_addr) >>
|
||||
EFI_PAGE_SHIFT;
|
||||
/* middle part */
|
||||
new += memmap.desc_size;
|
||||
memcpy(new, old, memmap.desc_size);
|
||||
new += efi.memmap.desc_size;
|
||||
memcpy(new, old, efi.memmap.desc_size);
|
||||
md = new;
|
||||
md->attribute |= m_attr;
|
||||
md->phys_addr = m_start;
|
||||
md->num_pages = (m_end - m_start + 1) >>
|
||||
EFI_PAGE_SHIFT;
|
||||
/* last part */
|
||||
new += memmap.desc_size;
|
||||
memcpy(new, old, memmap.desc_size);
|
||||
new += efi.memmap.desc_size;
|
||||
memcpy(new, old, efi.memmap.desc_size);
|
||||
md = new;
|
||||
md->phys_addr = m_end + 1;
|
||||
md->num_pages = (end - m_end) >>
|
||||
|
@ -169,8 +168,8 @@ void __init efi_fake_memmap(void)
|
|||
md->num_pages = (m_start - md->phys_addr) >>
|
||||
EFI_PAGE_SHIFT;
|
||||
/* latter part */
|
||||
new += memmap.desc_size;
|
||||
memcpy(new, old, memmap.desc_size);
|
||||
new += efi.memmap.desc_size;
|
||||
memcpy(new, old, efi.memmap.desc_size);
|
||||
md = new;
|
||||
md->phys_addr = m_start;
|
||||
md->num_pages = (end - md->phys_addr + 1) >>
|
||||
|
@ -182,10 +181,10 @@ void __init efi_fake_memmap(void)
|
|||
|
||||
/* swap into new EFI memmap */
|
||||
efi_unmap_memmap();
|
||||
memmap.map = new_memmap;
|
||||
memmap.phys_map = new_memmap_phy;
|
||||
memmap.nr_map = new_nr_map;
|
||||
memmap.map_end = memmap.map + memmap.nr_map * memmap.desc_size;
|
||||
efi.memmap.map = new_memmap;
|
||||
efi.memmap.phys_map = new_memmap_phy;
|
||||
efi.memmap.nr_map = new_nr_map;
|
||||
efi.memmap.map_end = efi.memmap.map + efi.memmap.nr_map * efi.memmap.desc_size;
|
||||
set_bit(EFI_MEMMAP, &efi.flags);
|
||||
|
||||
/* print new EFI memmap */
|
||||
|
|
|
@ -28,7 +28,7 @@ OBJECT_FILES_NON_STANDARD := y
|
|||
# Prevents link failures: __sanitizer_cov_trace_pc() is not linked in.
|
||||
KCOV_INSTRUMENT := n
|
||||
|
||||
lib-y := efi-stub-helper.o
|
||||
lib-y := efi-stub-helper.o gop.o
|
||||
|
||||
# include the stub's generic dependencies from lib/ when building for ARM/arm64
|
||||
arm-deps := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c sort.c
|
||||
|
|
|
@ -20,27 +20,49 @@
|
|||
|
||||
bool __nokaslr;
|
||||
|
||||
static int efi_secureboot_enabled(efi_system_table_t *sys_table_arg)
|
||||
static int efi_get_secureboot(efi_system_table_t *sys_table_arg)
|
||||
{
|
||||
static efi_guid_t const var_guid = EFI_GLOBAL_VARIABLE_GUID;
|
||||
static efi_char16_t const var_name[] = {
|
||||
static efi_char16_t const sb_var_name[] = {
|
||||
'S', 'e', 'c', 'u', 'r', 'e', 'B', 'o', 'o', 't', 0 };
|
||||
static efi_char16_t const sm_var_name[] = {
|
||||
'S', 'e', 't', 'u', 'p', 'M', 'o', 'd', 'e', 0 };
|
||||
|
||||
efi_guid_t var_guid = EFI_GLOBAL_VARIABLE_GUID;
|
||||
efi_get_variable_t *f_getvar = sys_table_arg->runtime->get_variable;
|
||||
unsigned long size = sizeof(u8);
|
||||
efi_status_t status;
|
||||
u8 val;
|
||||
unsigned long size = sizeof(val);
|
||||
efi_status_t status;
|
||||
|
||||
status = f_getvar((efi_char16_t *)var_name, (efi_guid_t *)&var_guid,
|
||||
status = f_getvar((efi_char16_t *)sb_var_name, (efi_guid_t *)&var_guid,
|
||||
NULL, &size, &val);
|
||||
|
||||
if (status != EFI_SUCCESS)
|
||||
goto out_efi_err;
|
||||
|
||||
if (val == 0)
|
||||
return 0;
|
||||
|
||||
status = f_getvar((efi_char16_t *)sm_var_name, (efi_guid_t *)&var_guid,
|
||||
NULL, &size, &val);
|
||||
|
||||
if (status != EFI_SUCCESS)
|
||||
goto out_efi_err;
|
||||
|
||||
if (val == 1)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
|
||||
out_efi_err:
|
||||
switch (status) {
|
||||
case EFI_SUCCESS:
|
||||
return val;
|
||||
case EFI_NOT_FOUND:
|
||||
return 0;
|
||||
case EFI_DEVICE_ERROR:
|
||||
return -EIO;
|
||||
case EFI_SECURITY_VIOLATION:
|
||||
return -EACCES;
|
||||
default:
|
||||
return 1;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -147,6 +169,25 @@ void efi_char16_printk(efi_system_table_t *sys_table_arg,
|
|||
out->output_string(out, str);
|
||||
}
|
||||
|
||||
static struct screen_info *setup_graphics(efi_system_table_t *sys_table_arg)
|
||||
{
|
||||
efi_guid_t gop_proto = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
|
||||
efi_status_t status;
|
||||
unsigned long size;
|
||||
void **gop_handle = NULL;
|
||||
struct screen_info *si = NULL;
|
||||
|
||||
size = 0;
|
||||
status = efi_call_early(locate_handle, EFI_LOCATE_BY_PROTOCOL,
|
||||
&gop_proto, NULL, &size, gop_handle);
|
||||
if (status == EFI_BUFFER_TOO_SMALL) {
|
||||
si = alloc_screen_info(sys_table_arg);
|
||||
if (!si)
|
||||
return NULL;
|
||||
efi_setup_gop(sys_table_arg, si, &gop_proto, size);
|
||||
}
|
||||
return si;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function handles the architcture specific differences between arm and
|
||||
|
@ -185,6 +226,8 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,
|
|||
efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID;
|
||||
unsigned long reserve_addr = 0;
|
||||
unsigned long reserve_size = 0;
|
||||
int secure_boot = 0;
|
||||
struct screen_info *si;
|
||||
|
||||
/* Check if we were booted by the EFI firmware */
|
||||
if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
|
||||
|
@ -237,6 +280,8 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,
|
|||
__nokaslr = true;
|
||||
}
|
||||
|
||||
si = setup_graphics(sys_table);
|
||||
|
||||
status = handle_kernel_image(sys_table, image_addr, &image_size,
|
||||
&reserve_addr,
|
||||
&reserve_size,
|
||||
|
@ -250,12 +295,21 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,
|
|||
if (status != EFI_SUCCESS)
|
||||
pr_efi_err(sys_table, "Failed to parse EFI cmdline options\n");
|
||||
|
||||
secure_boot = efi_get_secureboot(sys_table);
|
||||
if (secure_boot > 0)
|
||||
pr_efi(sys_table, "UEFI Secure Boot is enabled.\n");
|
||||
|
||||
if (secure_boot < 0) {
|
||||
pr_efi_err(sys_table,
|
||||
"could not determine UEFI Secure Boot status.\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Unauthenticated device tree data is a security hazard, so
|
||||
* ignore 'dtb=' unless UEFI Secure Boot is disabled.
|
||||
*/
|
||||
if (efi_secureboot_enabled(sys_table)) {
|
||||
pr_efi(sys_table, "UEFI Secure Boot is enabled.\n");
|
||||
if (secure_boot != 0 && strstr(cmdline_ptr, "dtb=")) {
|
||||
pr_efi(sys_table, "Ignoring DTB from command line.\n");
|
||||
} else {
|
||||
status = handle_cmdline_files(sys_table, image, cmdline_ptr,
|
||||
"dtb=",
|
||||
|
@ -309,6 +363,7 @@ fail_free_image:
|
|||
efi_free(sys_table, image_size, *image_addr);
|
||||
efi_free(sys_table, reserve_size, reserve_addr);
|
||||
fail_free_cmdline:
|
||||
free_screen_info(sys_table, si);
|
||||
efi_free(sys_table, cmdline_size, (unsigned long)cmdline_ptr);
|
||||
fail:
|
||||
return EFI_ERROR;
|
||||
|
|
|
@ -26,6 +26,43 @@ efi_status_t check_platform_features(efi_system_table_t *sys_table_arg)
|
|||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static efi_guid_t screen_info_guid = LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID;
|
||||
|
||||
struct screen_info *alloc_screen_info(efi_system_table_t *sys_table_arg)
|
||||
{
|
||||
struct screen_info *si;
|
||||
efi_status_t status;
|
||||
|
||||
/*
|
||||
* Unlike on arm64, where we can directly fill out the screen_info
|
||||
* structure from the stub, we need to allocate a buffer to hold
|
||||
* its contents while we hand over to the kernel proper from the
|
||||
* decompressor.
|
||||
*/
|
||||
status = efi_call_early(allocate_pool, EFI_RUNTIME_SERVICES_DATA,
|
||||
sizeof(*si), (void **)&si);
|
||||
|
||||
if (status != EFI_SUCCESS)
|
||||
return NULL;
|
||||
|
||||
status = efi_call_early(install_configuration_table,
|
||||
&screen_info_guid, si);
|
||||
if (status == EFI_SUCCESS)
|
||||
return si;
|
||||
|
||||
efi_call_early(free_pool, si);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void free_screen_info(efi_system_table_t *sys_table_arg, struct screen_info *si)
|
||||
{
|
||||
if (!si)
|
||||
return;
|
||||
|
||||
efi_call_early(install_configuration_table, &screen_info_guid, NULL);
|
||||
efi_call_early(free_pool, si);
|
||||
}
|
||||
|
||||
efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
|
||||
unsigned long *image_addr,
|
||||
unsigned long *image_size,
|
||||
|
|
|
@ -125,10 +125,12 @@ unsigned long get_dram_base(efi_system_table_t *sys_table_arg)
|
|||
|
||||
map.map_end = map.map + map_size;
|
||||
|
||||
for_each_efi_memory_desc(&map, md)
|
||||
if (md->attribute & EFI_MEMORY_WB)
|
||||
for_each_efi_memory_desc_in_map(&map, md) {
|
||||
if (md->attribute & EFI_MEMORY_WB) {
|
||||
if (membase > md->phys_addr)
|
||||
membase = md->phys_addr;
|
||||
}
|
||||
}
|
||||
|
||||
efi_call_early(free_pool, map.map);
|
||||
|
||||
|
|
|
@ -0,0 +1,354 @@
|
|||
/* -----------------------------------------------------------------------
|
||||
*
|
||||
* Copyright 2011 Intel Corporation; author Matt Fleming
|
||||
*
|
||||
* This file is part of the Linux kernel, and is made available under
|
||||
* the terms of the GNU General Public License version 2.
|
||||
*
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
#include <linux/efi.h>
|
||||
#include <linux/screen_info.h>
|
||||
#include <asm/efi.h>
|
||||
#include <asm/setup.h>
|
||||
|
||||
static void find_bits(unsigned long mask, u8 *pos, u8 *size)
|
||||
{
|
||||
u8 first, len;
|
||||
|
||||
first = 0;
|
||||
len = 0;
|
||||
|
||||
if (mask) {
|
||||
while (!(mask & 0x1)) {
|
||||
mask = mask >> 1;
|
||||
first++;
|
||||
}
|
||||
|
||||
while (mask & 0x1) {
|
||||
mask = mask >> 1;
|
||||
len++;
|
||||
}
|
||||
}
|
||||
|
||||
*pos = first;
|
||||
*size = len;
|
||||
}
|
||||
|
||||
static void
|
||||
setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
|
||||
struct efi_pixel_bitmask pixel_info, int pixel_format)
|
||||
{
|
||||
if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
|
||||
si->lfb_depth = 32;
|
||||
si->lfb_linelength = pixels_per_scan_line * 4;
|
||||
si->red_size = 8;
|
||||
si->red_pos = 0;
|
||||
si->green_size = 8;
|
||||
si->green_pos = 8;
|
||||
si->blue_size = 8;
|
||||
si->blue_pos = 16;
|
||||
si->rsvd_size = 8;
|
||||
si->rsvd_pos = 24;
|
||||
} else if (pixel_format == PIXEL_BGR_RESERVED_8BIT_PER_COLOR) {
|
||||
si->lfb_depth = 32;
|
||||
si->lfb_linelength = pixels_per_scan_line * 4;
|
||||
si->red_size = 8;
|
||||
si->red_pos = 16;
|
||||
si->green_size = 8;
|
||||
si->green_pos = 8;
|
||||
si->blue_size = 8;
|
||||
si->blue_pos = 0;
|
||||
si->rsvd_size = 8;
|
||||
si->rsvd_pos = 24;
|
||||
} else if (pixel_format == PIXEL_BIT_MASK) {
|
||||
find_bits(pixel_info.red_mask, &si->red_pos, &si->red_size);
|
||||
find_bits(pixel_info.green_mask, &si->green_pos,
|
||||
&si->green_size);
|
||||
find_bits(pixel_info.blue_mask, &si->blue_pos, &si->blue_size);
|
||||
find_bits(pixel_info.reserved_mask, &si->rsvd_pos,
|
||||
&si->rsvd_size);
|
||||
si->lfb_depth = si->red_size + si->green_size +
|
||||
si->blue_size + si->rsvd_size;
|
||||
si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
|
||||
} else {
|
||||
si->lfb_depth = 4;
|
||||
si->lfb_linelength = si->lfb_width / 2;
|
||||
si->red_size = 0;
|
||||
si->red_pos = 0;
|
||||
si->green_size = 0;
|
||||
si->green_pos = 0;
|
||||
si->blue_size = 0;
|
||||
si->blue_pos = 0;
|
||||
si->rsvd_size = 0;
|
||||
si->rsvd_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static efi_status_t
|
||||
__gop_query32(efi_system_table_t *sys_table_arg,
|
||||
struct efi_graphics_output_protocol_32 *gop32,
|
||||
struct efi_graphics_output_mode_info **info,
|
||||
unsigned long *size, u64 *fb_base)
|
||||
{
|
||||
struct efi_graphics_output_protocol_mode_32 *mode;
|
||||
efi_graphics_output_protocol_query_mode query_mode;
|
||||
efi_status_t status;
|
||||
unsigned long m;
|
||||
|
||||
m = gop32->mode;
|
||||
mode = (struct efi_graphics_output_protocol_mode_32 *)m;
|
||||
query_mode = (void *)(unsigned long)gop32->query_mode;
|
||||
|
||||
status = __efi_call_early(query_mode, (void *)gop32, mode->mode, size,
|
||||
info);
|
||||
if (status != EFI_SUCCESS)
|
||||
return status;
|
||||
|
||||
*fb_base = mode->frame_buffer_base;
|
||||
return status;
|
||||
}
|
||||
|
||||
static efi_status_t
|
||||
setup_gop32(efi_system_table_t *sys_table_arg, struct screen_info *si,
|
||||
efi_guid_t *proto, unsigned long size, void **gop_handle)
|
||||
{
|
||||
struct efi_graphics_output_protocol_32 *gop32, *first_gop;
|
||||
unsigned long nr_gops;
|
||||
u16 width, height;
|
||||
u32 pixels_per_scan_line;
|
||||
u32 ext_lfb_base;
|
||||
u64 fb_base;
|
||||
struct efi_pixel_bitmask pixel_info;
|
||||
int pixel_format;
|
||||
efi_status_t status = EFI_NOT_FOUND;
|
||||
u32 *handles = (u32 *)(unsigned long)gop_handle;
|
||||
int i;
|
||||
|
||||
first_gop = NULL;
|
||||
gop32 = NULL;
|
||||
|
||||
nr_gops = size / sizeof(u32);
|
||||
for (i = 0; i < nr_gops; i++) {
|
||||
struct efi_graphics_output_mode_info *info = NULL;
|
||||
efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
|
||||
bool conout_found = false;
|
||||
void *dummy = NULL;
|
||||
efi_handle_t h = (efi_handle_t)(unsigned long)handles[i];
|
||||
u64 current_fb_base;
|
||||
|
||||
status = efi_call_early(handle_protocol, h,
|
||||
proto, (void **)&gop32);
|
||||
if (status != EFI_SUCCESS)
|
||||
continue;
|
||||
|
||||
status = efi_call_early(handle_protocol, h,
|
||||
&conout_proto, &dummy);
|
||||
if (status == EFI_SUCCESS)
|
||||
conout_found = true;
|
||||
|
||||
status = __gop_query32(sys_table_arg, gop32, &info, &size,
|
||||
¤t_fb_base);
|
||||
if (status == EFI_SUCCESS && (!first_gop || conout_found)) {
|
||||
/*
|
||||
* Systems that use the UEFI Console Splitter may
|
||||
* provide multiple GOP devices, not all of which are
|
||||
* backed by real hardware. The workaround is to search
|
||||
* for a GOP implementing the ConOut protocol, and if
|
||||
* one isn't found, to just fall back to the first GOP.
|
||||
*/
|
||||
width = info->horizontal_resolution;
|
||||
height = info->vertical_resolution;
|
||||
pixel_format = info->pixel_format;
|
||||
pixel_info = info->pixel_information;
|
||||
pixels_per_scan_line = info->pixels_per_scan_line;
|
||||
fb_base = current_fb_base;
|
||||
|
||||
/*
|
||||
* Once we've found a GOP supporting ConOut,
|
||||
* don't bother looking any further.
|
||||
*/
|
||||
first_gop = gop32;
|
||||
if (conout_found)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Did we find any GOPs? */
|
||||
if (!first_gop)
|
||||
goto out;
|
||||
|
||||
/* EFI framebuffer */
|
||||
si->orig_video_isVGA = VIDEO_TYPE_EFI;
|
||||
|
||||
si->lfb_width = width;
|
||||
si->lfb_height = height;
|
||||
si->lfb_base = fb_base;
|
||||
|
||||
ext_lfb_base = (u64)(unsigned long)fb_base >> 32;
|
||||
if (ext_lfb_base) {
|
||||
si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
|
||||
si->ext_lfb_base = ext_lfb_base;
|
||||
}
|
||||
|
||||
si->pages = 1;
|
||||
|
||||
setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format);
|
||||
|
||||
si->lfb_size = si->lfb_linelength * si->lfb_height;
|
||||
|
||||
si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static efi_status_t
|
||||
__gop_query64(efi_system_table_t *sys_table_arg,
|
||||
struct efi_graphics_output_protocol_64 *gop64,
|
||||
struct efi_graphics_output_mode_info **info,
|
||||
unsigned long *size, u64 *fb_base)
|
||||
{
|
||||
struct efi_graphics_output_protocol_mode_64 *mode;
|
||||
efi_graphics_output_protocol_query_mode query_mode;
|
||||
efi_status_t status;
|
||||
unsigned long m;
|
||||
|
||||
m = gop64->mode;
|
||||
mode = (struct efi_graphics_output_protocol_mode_64 *)m;
|
||||
query_mode = (void *)(unsigned long)gop64->query_mode;
|
||||
|
||||
status = __efi_call_early(query_mode, (void *)gop64, mode->mode, size,
|
||||
info);
|
||||
if (status != EFI_SUCCESS)
|
||||
return status;
|
||||
|
||||
*fb_base = mode->frame_buffer_base;
|
||||
return status;
|
||||
}
|
||||
|
||||
static efi_status_t
|
||||
setup_gop64(efi_system_table_t *sys_table_arg, struct screen_info *si,
|
||||
efi_guid_t *proto, unsigned long size, void **gop_handle)
|
||||
{
|
||||
struct efi_graphics_output_protocol_64 *gop64, *first_gop;
|
||||
unsigned long nr_gops;
|
||||
u16 width, height;
|
||||
u32 pixels_per_scan_line;
|
||||
u32 ext_lfb_base;
|
||||
u64 fb_base;
|
||||
struct efi_pixel_bitmask pixel_info;
|
||||
int pixel_format;
|
||||
efi_status_t status = EFI_NOT_FOUND;
|
||||
u64 *handles = (u64 *)(unsigned long)gop_handle;
|
||||
int i;
|
||||
|
||||
first_gop = NULL;
|
||||
gop64 = NULL;
|
||||
|
||||
nr_gops = size / sizeof(u64);
|
||||
for (i = 0; i < nr_gops; i++) {
|
||||
struct efi_graphics_output_mode_info *info = NULL;
|
||||
efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
|
||||
bool conout_found = false;
|
||||
void *dummy = NULL;
|
||||
efi_handle_t h = (efi_handle_t)(unsigned long)handles[i];
|
||||
u64 current_fb_base;
|
||||
|
||||
status = efi_call_early(handle_protocol, h,
|
||||
proto, (void **)&gop64);
|
||||
if (status != EFI_SUCCESS)
|
||||
continue;
|
||||
|
||||
status = efi_call_early(handle_protocol, h,
|
||||
&conout_proto, &dummy);
|
||||
if (status == EFI_SUCCESS)
|
||||
conout_found = true;
|
||||
|
||||
status = __gop_query64(sys_table_arg, gop64, &info, &size,
|
||||
¤t_fb_base);
|
||||
if (status == EFI_SUCCESS && (!first_gop || conout_found)) {
|
||||
/*
|
||||
* Systems that use the UEFI Console Splitter may
|
||||
* provide multiple GOP devices, not all of which are
|
||||
* backed by real hardware. The workaround is to search
|
||||
* for a GOP implementing the ConOut protocol, and if
|
||||
* one isn't found, to just fall back to the first GOP.
|
||||
*/
|
||||
width = info->horizontal_resolution;
|
||||
height = info->vertical_resolution;
|
||||
pixel_format = info->pixel_format;
|
||||
pixel_info = info->pixel_information;
|
||||
pixels_per_scan_line = info->pixels_per_scan_line;
|
||||
fb_base = current_fb_base;
|
||||
|
||||
/*
|
||||
* Once we've found a GOP supporting ConOut,
|
||||
* don't bother looking any further.
|
||||
*/
|
||||
first_gop = gop64;
|
||||
if (conout_found)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Did we find any GOPs? */
|
||||
if (!first_gop)
|
||||
goto out;
|
||||
|
||||
/* EFI framebuffer */
|
||||
si->orig_video_isVGA = VIDEO_TYPE_EFI;
|
||||
|
||||
si->lfb_width = width;
|
||||
si->lfb_height = height;
|
||||
si->lfb_base = fb_base;
|
||||
|
||||
ext_lfb_base = (u64)(unsigned long)fb_base >> 32;
|
||||
if (ext_lfb_base) {
|
||||
si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
|
||||
si->ext_lfb_base = ext_lfb_base;
|
||||
}
|
||||
|
||||
si->pages = 1;
|
||||
|
||||
setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format);
|
||||
|
||||
si->lfb_size = si->lfb_linelength * si->lfb_height;
|
||||
|
||||
si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* See if we have Graphics Output Protocol
|
||||
*/
|
||||
efi_status_t efi_setup_gop(efi_system_table_t *sys_table_arg,
|
||||
struct screen_info *si, efi_guid_t *proto,
|
||||
unsigned long size)
|
||||
{
|
||||
efi_status_t status;
|
||||
void **gop_handle = NULL;
|
||||
|
||||
status = efi_call_early(allocate_pool, EFI_LOADER_DATA,
|
||||
size, (void **)&gop_handle);
|
||||
if (status != EFI_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = efi_call_early(locate_handle,
|
||||
EFI_LOCATE_BY_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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -9,7 +9,8 @@ int efi_reboot_quirk_mode = -1;
|
|||
|
||||
void efi_reboot(enum reboot_mode reboot_mode, const char *__unused)
|
||||
{
|
||||
int efi_mode;
|
||||
const char *str[] = { "cold", "warm", "shutdown", "platform" };
|
||||
int efi_mode, cap_reset_mode;
|
||||
|
||||
if (!efi_enabled(EFI_RUNTIME_SERVICES))
|
||||
return;
|
||||
|
@ -30,6 +31,15 @@ void efi_reboot(enum reboot_mode reboot_mode, const char *__unused)
|
|||
if (efi_reboot_quirk_mode != -1)
|
||||
efi_mode = efi_reboot_quirk_mode;
|
||||
|
||||
if (efi_capsule_pending(&cap_reset_mode)) {
|
||||
if (efi_mode != cap_reset_mode)
|
||||
printk(KERN_CRIT "efi: %s reset requested but pending "
|
||||
"capsule update requires %s reset... Performing "
|
||||
"%s reset.\n", str[efi_mode], str[cap_reset_mode],
|
||||
str[cap_reset_mode]);
|
||||
efi_mode = cap_reset_mode;
|
||||
}
|
||||
|
||||
efi.reset_system(efi_mode, EFI_SUCCESS, 0, NULL);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,10 +16,70 @@
|
|||
|
||||
#include <linux/bug.h>
|
||||
#include <linux/efi.h>
|
||||
#include <linux/irqflags.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/stringify.h>
|
||||
#include <asm/efi.h>
|
||||
|
||||
static void efi_call_virt_check_flags(unsigned long flags, const char *call)
|
||||
{
|
||||
unsigned long cur_flags, mismatch;
|
||||
|
||||
local_save_flags(cur_flags);
|
||||
|
||||
mismatch = flags ^ cur_flags;
|
||||
if (!WARN_ON_ONCE(mismatch & ARCH_EFI_IRQ_FLAGS_MASK))
|
||||
return;
|
||||
|
||||
add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_NOW_UNRELIABLE);
|
||||
pr_err_ratelimited(FW_BUG "IRQ flags corrupted (0x%08lx=>0x%08lx) by EFI %s\n",
|
||||
flags, cur_flags, call);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Arch code can implement the following three template macros, avoiding
|
||||
* reptition for the void/non-void return cases of {__,}efi_call_virt:
|
||||
*
|
||||
* * arch_efi_call_virt_setup
|
||||
*
|
||||
* Sets up the environment for the call (e.g. switching page tables,
|
||||
* allowing kernel-mode use of floating point, if required).
|
||||
*
|
||||
* * arch_efi_call_virt
|
||||
*
|
||||
* Performs the call. The last expression in the macro must be the call
|
||||
* itself, allowing the logic to be shared by the void and non-void
|
||||
* cases.
|
||||
*
|
||||
* * arch_efi_call_virt_teardown
|
||||
*
|
||||
* Restores the usual kernel environment once the call has returned.
|
||||
*/
|
||||
|
||||
#define efi_call_virt(f, args...) \
|
||||
({ \
|
||||
efi_status_t __s; \
|
||||
unsigned long flags; \
|
||||
arch_efi_call_virt_setup(); \
|
||||
local_save_flags(flags); \
|
||||
__s = arch_efi_call_virt(f, args); \
|
||||
efi_call_virt_check_flags(flags, __stringify(f)); \
|
||||
arch_efi_call_virt_teardown(); \
|
||||
__s; \
|
||||
})
|
||||
|
||||
#define __efi_call_virt(f, args...) \
|
||||
({ \
|
||||
unsigned long flags; \
|
||||
arch_efi_call_virt_setup(); \
|
||||
local_save_flags(flags); \
|
||||
arch_efi_call_virt(f, args); \
|
||||
efi_call_virt_check_flags(flags, __stringify(f)); \
|
||||
arch_efi_call_virt_teardown(); \
|
||||
})
|
||||
|
||||
/*
|
||||
* According to section 7.1 of the UEFI spec, Runtime Services are not fully
|
||||
* reentrant, and there are particular combinations of calls that need to be
|
||||
|
|
|
@ -329,39 +329,6 @@ check_var_size_nonblocking(u32 attributes, unsigned long size)
|
|||
return fops->query_variable_store(attributes, size, true);
|
||||
}
|
||||
|
||||
static int efi_status_to_err(efi_status_t status)
|
||||
{
|
||||
int err;
|
||||
|
||||
switch (status) {
|
||||
case EFI_SUCCESS:
|
||||
err = 0;
|
||||
break;
|
||||
case EFI_INVALID_PARAMETER:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
case EFI_OUT_OF_RESOURCES:
|
||||
err = -ENOSPC;
|
||||
break;
|
||||
case EFI_DEVICE_ERROR:
|
||||
err = -EIO;
|
||||
break;
|
||||
case EFI_WRITE_PROTECTED:
|
||||
err = -EROFS;
|
||||
break;
|
||||
case EFI_SECURITY_VIOLATION:
|
||||
err = -EACCES;
|
||||
break;
|
||||
case EFI_NOT_FOUND:
|
||||
err = -ENOENT;
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor,
|
||||
struct list_head *head)
|
||||
{
|
||||
|
@ -452,8 +419,7 @@ static void dup_variable_bug(efi_char16_t *str16, efi_guid_t *vendor_guid,
|
|||
* Returns 0 on success, or a kernel error code on failure.
|
||||
*/
|
||||
int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
|
||||
void *data, bool atomic, bool duplicates,
|
||||
struct list_head *head)
|
||||
void *data, bool duplicates, struct list_head *head)
|
||||
{
|
||||
const struct efivar_operations *ops = __efivars->ops;
|
||||
unsigned long variable_name_size = 1024;
|
||||
|
@ -483,7 +449,7 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
|
|||
&vendor_guid);
|
||||
switch (status) {
|
||||
case EFI_SUCCESS:
|
||||
if (!atomic)
|
||||
if (duplicates)
|
||||
spin_unlock_irq(&__efivars->lock);
|
||||
|
||||
variable_name_size = var_name_strnsize(variable_name,
|
||||
|
@ -498,21 +464,19 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
|
|||
* and may end up looping here forever.
|
||||
*/
|
||||
if (duplicates &&
|
||||
variable_is_present(variable_name, &vendor_guid, head)) {
|
||||
variable_is_present(variable_name, &vendor_guid,
|
||||
head)) {
|
||||
dup_variable_bug(variable_name, &vendor_guid,
|
||||
variable_name_size);
|
||||
if (!atomic)
|
||||
spin_lock_irq(&__efivars->lock);
|
||||
|
||||
status = EFI_NOT_FOUND;
|
||||
break;
|
||||
}
|
||||
|
||||
err = func(variable_name, vendor_guid, variable_name_size, data);
|
||||
} else {
|
||||
err = func(variable_name, vendor_guid,
|
||||
variable_name_size, data);
|
||||
if (err)
|
||||
status = EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (!atomic)
|
||||
if (duplicates)
|
||||
spin_lock_irq(&__efivars->lock);
|
||||
|
||||
break;
|
||||
|
|
|
@ -761,7 +761,7 @@ config FB_VESA
|
|||
|
||||
config FB_EFI
|
||||
bool "EFI-based Framebuffer Support"
|
||||
depends on (FB = y) && X86 && EFI
|
||||
depends on (FB = y) && !IA64 && EFI
|
||||
select FB_CFB_FILLRECT
|
||||
select FB_CFB_COPYAREA
|
||||
select FB_CFB_IMAGEBLIT
|
||||
|
|
|
@ -6,16 +6,14 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/efi.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/screen_info.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/pci.h>
|
||||
#include <video/vga.h>
|
||||
#include <asm/sysfb.h>
|
||||
#include <asm/efi.h>
|
||||
|
||||
static bool request_mem_succeeded = false;
|
||||
|
||||
|
@ -85,21 +83,13 @@ static struct fb_ops efifb_ops = {
|
|||
static int efifb_setup(char *options)
|
||||
{
|
||||
char *this_opt;
|
||||
int i;
|
||||
|
||||
if (options && *options) {
|
||||
while ((this_opt = strsep(&options, ",")) != NULL) {
|
||||
if (!*this_opt) continue;
|
||||
|
||||
for (i = 0; i < M_UNKNOWN; i++) {
|
||||
if (efifb_dmi_list[i].base != 0 &&
|
||||
!strcmp(this_opt, efifb_dmi_list[i].optname)) {
|
||||
screen_info.lfb_base = efifb_dmi_list[i].base;
|
||||
screen_info.lfb_linelength = efifb_dmi_list[i].stride;
|
||||
screen_info.lfb_width = efifb_dmi_list[i].width;
|
||||
screen_info.lfb_height = efifb_dmi_list[i].height;
|
||||
}
|
||||
}
|
||||
efifb_setup_from_dmi(&screen_info, this_opt);
|
||||
|
||||
if (!strncmp(this_opt, "base:", 5))
|
||||
screen_info.lfb_base = simple_strtoul(this_opt+5, NULL, 0);
|
||||
else if (!strncmp(this_opt, "stride:", 7))
|
||||
|
@ -338,5 +328,4 @@ static struct platform_driver efifb_driver = {
|
|||
.remove = efifb_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(efifb_driver);
|
||||
MODULE_LICENSE("GPL");
|
||||
builtin_platform_driver(efifb_driver);
|
||||
|
|
|
@ -316,7 +316,6 @@ static const struct efi efi_xen __initconst = {
|
|||
.get_next_high_mono_count = xen_efi_get_next_high_mono_count,
|
||||
.reset_system = NULL, /* Functionality provided by Xen. */
|
||||
.set_virtual_address_map = NULL, /* Not used under Xen. */
|
||||
.memmap = NULL, /* Not used under Xen. */
|
||||
.flags = 0 /* Initialized later. */
|
||||
};
|
||||
|
||||
|
|
|
@ -157,7 +157,7 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
long
|
||||
static long
|
||||
efivarfs_file_ioctl(struct file *file, unsigned int cmd, unsigned long p)
|
||||
{
|
||||
void __user *arg = (void __user *)p;
|
||||
|
|
|
@ -216,8 +216,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
|
|||
|
||||
INIT_LIST_HEAD(&efivarfs_list);
|
||||
|
||||
err = efivar_init(efivarfs_callback, (void *)sb, false,
|
||||
true, &efivarfs_list);
|
||||
err = efivar_init(efivarfs_callback, (void *)sb, true, &efivarfs_list);
|
||||
if (err)
|
||||
__efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL, NULL);
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/pfn.h>
|
||||
#include <linux/pstore.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/screen_info.h>
|
||||
|
||||
#include <asm/page.h>
|
||||
|
||||
|
@ -123,6 +124,13 @@ typedef struct {
|
|||
u32 imagesize;
|
||||
} efi_capsule_header_t;
|
||||
|
||||
/*
|
||||
* EFI capsule flags
|
||||
*/
|
||||
#define EFI_CAPSULE_PERSIST_ACROSS_RESET 0x00010000
|
||||
#define EFI_CAPSULE_POPULATE_SYSTEM_TABLE 0x00020000
|
||||
#define EFI_CAPSULE_INITIATE_RESET 0x00040000
|
||||
|
||||
/*
|
||||
* Allocation types for calls to boottime->allocate_pages.
|
||||
*/
|
||||
|
@ -282,9 +290,10 @@ typedef struct {
|
|||
efi_status_t (*handle_protocol)(efi_handle_t, efi_guid_t *, void **);
|
||||
void *__reserved;
|
||||
void *register_protocol_notify;
|
||||
void *locate_handle;
|
||||
efi_status_t (*locate_handle)(int, efi_guid_t *, void *,
|
||||
unsigned long *, efi_handle_t *);
|
||||
void *locate_device_path;
|
||||
void *install_configuration_table;
|
||||
efi_status_t (*install_configuration_table)(efi_guid_t *, void *);
|
||||
void *load_image;
|
||||
void *start_image;
|
||||
void *exit;
|
||||
|
@ -623,6 +632,27 @@ void efi_native_runtime_setup(void);
|
|||
EFI_GUID(0x3152bca5, 0xeade, 0x433d, \
|
||||
0x86, 0x2e, 0xc0, 0x1c, 0xdc, 0x29, 0x1f, 0x44)
|
||||
|
||||
#define EFI_MEMORY_ATTRIBUTES_TABLE_GUID \
|
||||
EFI_GUID(0xdcfa911d, 0x26eb, 0x469f, \
|
||||
0xa2, 0x20, 0x38, 0xb7, 0xdc, 0x46, 0x12, 0x20)
|
||||
|
||||
#define EFI_CONSOLE_OUT_DEVICE_GUID \
|
||||
EFI_GUID(0xd3b36f2c, 0xd551, 0x11d4, \
|
||||
0x9a, 0x46, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d)
|
||||
|
||||
/*
|
||||
* This GUID is used to pass to the kernel proper the struct screen_info
|
||||
* structure that was populated by the stub based on the GOP protocol instance
|
||||
* associated with ConOut
|
||||
*/
|
||||
#define LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID \
|
||||
EFI_GUID(0xe03fc20a, 0x85dc, 0x406e, \
|
||||
0xb9, 0xe, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95)
|
||||
|
||||
#define LINUX_EFI_LOADER_ENTRY_GUID \
|
||||
EFI_GUID(0x4a67b082, 0x0a4c, 0x41cf, \
|
||||
0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f)
|
||||
|
||||
typedef struct {
|
||||
efi_guid_t guid;
|
||||
u64 table;
|
||||
|
@ -847,6 +877,14 @@ typedef struct {
|
|||
|
||||
#define EFI_INVALID_TABLE_ADDR (~0UL)
|
||||
|
||||
typedef struct {
|
||||
u32 version;
|
||||
u32 num_entries;
|
||||
u32 desc_size;
|
||||
u32 reserved;
|
||||
efi_memory_desc_t entry[0];
|
||||
} efi_memory_attributes_table_t;
|
||||
|
||||
/*
|
||||
* All runtime access to EFI goes through this structure:
|
||||
*/
|
||||
|
@ -868,6 +906,7 @@ extern struct efi {
|
|||
unsigned long config_table; /* config tables */
|
||||
unsigned long esrt; /* ESRT table */
|
||||
unsigned long properties_table; /* properties table */
|
||||
unsigned long mem_attr_table; /* memory attributes table */
|
||||
efi_get_time_t *get_time;
|
||||
efi_set_time_t *set_time;
|
||||
efi_get_wakeup_time_t *get_wakeup_time;
|
||||
|
@ -883,7 +922,7 @@ extern struct efi {
|
|||
efi_get_next_high_mono_count_t *get_next_high_mono_count;
|
||||
efi_reset_system_t *reset_system;
|
||||
efi_set_virtual_address_map_t *set_virtual_address_map;
|
||||
struct efi_memory_map *memmap;
|
||||
struct efi_memory_map memmap;
|
||||
unsigned long flags;
|
||||
} efi;
|
||||
|
||||
|
@ -945,7 +984,6 @@ extern void efi_initialize_iomem_resources(struct resource *code_resource,
|
|||
extern void efi_get_time(struct timespec *now);
|
||||
extern void efi_reserve_boot_services(void);
|
||||
extern int efi_get_fdt_params(struct efi_fdt_params *params);
|
||||
extern struct efi_memory_map memmap;
|
||||
extern struct kobject *efi_kobj;
|
||||
|
||||
extern int efi_reboot_quirk_mode;
|
||||
|
@ -957,12 +995,34 @@ extern void __init efi_fake_memmap(void);
|
|||
static inline void efi_fake_memmap(void) { }
|
||||
#endif
|
||||
|
||||
/*
|
||||
* efi_memattr_perm_setter - arch specific callback function passed into
|
||||
* efi_memattr_apply_permissions() that updates the
|
||||
* mapping permissions described by the second
|
||||
* argument in the page tables referred to by the
|
||||
* first argument.
|
||||
*/
|
||||
typedef int (*efi_memattr_perm_setter)(struct mm_struct *, efi_memory_desc_t *);
|
||||
|
||||
extern int efi_memattr_init(void);
|
||||
extern int efi_memattr_apply_permissions(struct mm_struct *mm,
|
||||
efi_memattr_perm_setter fn);
|
||||
|
||||
/* Iterate through an efi_memory_map */
|
||||
#define for_each_efi_memory_desc(m, md) \
|
||||
#define for_each_efi_memory_desc_in_map(m, md) \
|
||||
for ((md) = (m)->map; \
|
||||
(md) <= (efi_memory_desc_t *)((m)->map_end - (m)->desc_size); \
|
||||
(md) = (void *)(md) + (m)->desc_size)
|
||||
|
||||
/**
|
||||
* for_each_efi_memory_desc - iterate over descriptors in efi.memmap
|
||||
* @md: the efi_memory_desc_t * iterator
|
||||
*
|
||||
* Once the loop finishes @md must not be accessed.
|
||||
*/
|
||||
#define for_each_efi_memory_desc(md) \
|
||||
for_each_efi_memory_desc_in_map(&efi.memmap, md)
|
||||
|
||||
/*
|
||||
* Format an EFI memory descriptor's type and attributes to a user-provided
|
||||
* character buffer, as per snprintf(), and return the buffer.
|
||||
|
@ -1000,7 +1060,6 @@ extern int __init efi_setup_pcdp_console(char *);
|
|||
* possible, remove EFI-related code altogether.
|
||||
*/
|
||||
#define EFI_BOOT 0 /* Were we booted from EFI? */
|
||||
#define EFI_SYSTEM_TABLES 1 /* Can we use EFI system tables? */
|
||||
#define EFI_CONFIG_TABLES 2 /* Can we use EFI config tables? */
|
||||
#define EFI_RUNTIME_SERVICES 3 /* Can we use runtime services? */
|
||||
#define EFI_MEMMAP 4 /* Can we use EFI memory map? */
|
||||
|
@ -1026,8 +1085,16 @@ static inline bool efi_enabled(int feature)
|
|||
}
|
||||
static inline void
|
||||
efi_reboot(enum reboot_mode reboot_mode, const char *__unused) {}
|
||||
|
||||
static inline bool
|
||||
efi_capsule_pending(int *reset_type)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
extern int efi_status_to_err(efi_status_t status);
|
||||
|
||||
/*
|
||||
* Variable Attributes
|
||||
*/
|
||||
|
@ -1180,6 +1247,80 @@ struct efi_simple_text_output_protocol {
|
|||
void *test_string;
|
||||
};
|
||||
|
||||
#define PIXEL_RGB_RESERVED_8BIT_PER_COLOR 0
|
||||
#define PIXEL_BGR_RESERVED_8BIT_PER_COLOR 1
|
||||
#define PIXEL_BIT_MASK 2
|
||||
#define PIXEL_BLT_ONLY 3
|
||||
#define PIXEL_FORMAT_MAX 4
|
||||
|
||||
struct efi_pixel_bitmask {
|
||||
u32 red_mask;
|
||||
u32 green_mask;
|
||||
u32 blue_mask;
|
||||
u32 reserved_mask;
|
||||
};
|
||||
|
||||
struct efi_graphics_output_mode_info {
|
||||
u32 version;
|
||||
u32 horizontal_resolution;
|
||||
u32 vertical_resolution;
|
||||
int pixel_format;
|
||||
struct efi_pixel_bitmask pixel_information;
|
||||
u32 pixels_per_scan_line;
|
||||
} __packed;
|
||||
|
||||
struct efi_graphics_output_protocol_mode_32 {
|
||||
u32 max_mode;
|
||||
u32 mode;
|
||||
u32 info;
|
||||
u32 size_of_info;
|
||||
u64 frame_buffer_base;
|
||||
u32 frame_buffer_size;
|
||||
} __packed;
|
||||
|
||||
struct efi_graphics_output_protocol_mode_64 {
|
||||
u32 max_mode;
|
||||
u32 mode;
|
||||
u64 info;
|
||||
u64 size_of_info;
|
||||
u64 frame_buffer_base;
|
||||
u64 frame_buffer_size;
|
||||
} __packed;
|
||||
|
||||
struct efi_graphics_output_protocol_mode {
|
||||
u32 max_mode;
|
||||
u32 mode;
|
||||
unsigned long info;
|
||||
unsigned long size_of_info;
|
||||
u64 frame_buffer_base;
|
||||
unsigned long frame_buffer_size;
|
||||
} __packed;
|
||||
|
||||
struct efi_graphics_output_protocol_32 {
|
||||
u32 query_mode;
|
||||
u32 set_mode;
|
||||
u32 blt;
|
||||
u32 mode;
|
||||
};
|
||||
|
||||
struct efi_graphics_output_protocol_64 {
|
||||
u64 query_mode;
|
||||
u64 set_mode;
|
||||
u64 blt;
|
||||
u64 mode;
|
||||
};
|
||||
|
||||
struct efi_graphics_output_protocol {
|
||||
unsigned long query_mode;
|
||||
unsigned long set_mode;
|
||||
unsigned long blt;
|
||||
struct efi_graphics_output_protocol_mode *mode;
|
||||
};
|
||||
|
||||
typedef efi_status_t (*efi_graphics_output_protocol_query_mode)(
|
||||
struct efi_graphics_output_protocol *, u32, unsigned long *,
|
||||
struct efi_graphics_output_mode_info **);
|
||||
|
||||
extern struct list_head efivar_sysfs_list;
|
||||
|
||||
static inline void
|
||||
|
@ -1195,8 +1336,7 @@ int efivars_unregister(struct efivars *efivars);
|
|||
struct kobject *efivars_kobject(void);
|
||||
|
||||
int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
|
||||
void *data, bool atomic, bool duplicates,
|
||||
struct list_head *head);
|
||||
void *data, bool duplicates, struct list_head *head);
|
||||
|
||||
void efivar_entry_add(struct efivar_entry *entry, struct list_head *head);
|
||||
void efivar_entry_remove(struct efivar_entry *entry);
|
||||
|
@ -1242,6 +1382,13 @@ int efivars_sysfs_init(void);
|
|||
#define EFIVARS_DATA_SIZE_MAX 1024
|
||||
|
||||
#endif /* CONFIG_EFI_VARS */
|
||||
extern bool efi_capsule_pending(int *reset_type);
|
||||
|
||||
extern int efi_capsule_supported(efi_guid_t guid, u32 flags,
|
||||
size_t size, int *reset);
|
||||
|
||||
extern int efi_capsule_update(efi_capsule_header_t *capsule,
|
||||
struct page **pages);
|
||||
|
||||
#ifdef CONFIG_EFI_RUNTIME_MAP
|
||||
int efi_runtime_map_init(struct kobject *);
|
||||
|
@ -1319,5 +1466,9 @@ efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
|
|||
|
||||
efi_status_t efi_parse_options(char *cmdline);
|
||||
|
||||
efi_status_t efi_setup_gop(efi_system_table_t *sys_table_arg,
|
||||
struct screen_info *si, efi_guid_t *proto,
|
||||
unsigned long size);
|
||||
|
||||
bool efi_runtime_disabled(void);
|
||||
#endif /* _LINUX_EFI_H */
|
||||
|
|
Loading…
Reference in New Issue