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