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 EFI changes in this cycle are much larger than usual, for two
  (positive) reasons:

   - The GRUB project is showing signs of life again, resulting in the
     introduction of the generic Linux/UEFI boot protocol, instead of
     x86 specific hacks which are increasingly difficult to maintain.
     There's hope that all future extensions will now go through that
     boot protocol.

   - Preparatory work for RISC-V EFI support.

  The main changes are:

   - Boot time GDT handling changes

   - Simplify handling of EFI properties table on arm64

   - Generic EFI stub cleanups, to improve command line handling, file
     I/O, memory allocation, etc.

   - Introduce a generic initrd loading method based on calling back
     into the firmware, instead of relying on the x86 EFI handover
     protocol or device tree.

   - Introduce a mixed mode boot method that does not rely on the x86
     EFI handover protocol either, and could potentially be adopted by
     other architectures (if another one ever surfaces where one
     execution mode is a superset of another)

   - Clean up the contents of 'struct efi', and move out everything that
     doesn't need to be stored there.

   - Incorporate support for UEFI spec v2.8A changes that permit
     firmware implementations to return EFI_UNSUPPORTED from UEFI
     runtime services at OS runtime, and expose a mask of which ones are
     supported or unsupported via a configuration table.

   - Partial fix for the lack of by-VA cache maintenance in the
     decompressor on 32-bit ARM.

   - Changes to load device firmware from EFI boot service memory
     regions

   - Various documentation updates and minor code cleanups and fixes"

* 'efi-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (114 commits)
  efi/libstub/arm: Fix spurious message that an initrd was loaded
  efi/libstub/arm64: Avoid image_base value from efi_loaded_image
  partitions/efi: Fix partition name parsing in GUID partition entry
  efi/x86: Fix cast of image argument
  efi/libstub/x86: Use ULONG_MAX as upper bound for all allocations
  efi: Fix a mistype in comments mentioning efivar_entry_iter_begin()
  efi/libstub: Avoid linking libstub/lib-ksyms.o into vmlinux
  efi/x86: Preserve %ebx correctly in efi_set_virtual_address_map()
  efi/x86: Ignore the memory attributes table on i386
  efi/x86: Don't relocate the kernel unless necessary
  efi/x86: Remove extra headroom for setup block
  efi/x86: Add kernel preferred address to PE header
  efi/x86: Decompress at start of PE image load address
  x86/boot/compressed/32: Save the output address instead of recalculating it
  efi/libstub/x86: Deal with exit() boot service returning
  x86/boot: Use unsigned comparison for addresses
  efi/x86: Avoid using code32_start
  efi/x86: Make efi32_pe_entry() more readable
  efi/x86: Respect 32-bit ABI in efi32_pe_entry()
  efi/x86: Annotate the LOADED_IMAGE_PROTOCOL_GUID with SYM_DATA
  ...
This commit is contained in:
Linus Torvalds 2020-03-30 16:13:08 -07:00
commit a776c270a0
73 changed files with 2945 additions and 2697 deletions

View File

@ -0,0 +1,11 @@
.. SPDX-License-Identifier: GPL-2.0
============
UEFI Support
============
UEFI stub library functions
===========================
.. kernel-doc:: drivers/firmware/efi/libstub/mem.c
:internal:

View File

@ -6,6 +6,7 @@ Linux Firmware API
introduction introduction
core core
efi/index
request_firmware request_firmware
other_interfaces other_interfaces

View File

@ -490,15 +490,11 @@ Protocol: 2.00+
kernel) to not write early messages that require kernel) to not write early messages that require
accessing the display hardware directly. accessing the display hardware directly.
Bit 6 (write): KEEP_SEGMENTS Bit 6 (obsolete): KEEP_SEGMENTS
Protocol: 2.07+ Protocol: 2.07+
- If 0, reload the segment registers in the 32bit entry point. - This flag is obsolete.
- If 1, do not reload the segment registers in the 32bit entry point.
Assume that %cs %ds %ss %es are all set to flat segments with
a base of 0 (or the equivalent for their environment).
Bit 7 (write): CAN_USE_HEAP Bit 7 (write): CAN_USE_HEAP

View File

@ -6363,7 +6363,6 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi.git
S: Maintained S: Maintained
F: Documentation/admin-guide/efi-stub.rst F: Documentation/admin-guide/efi-stub.rst
F: arch/*/kernel/efi.c F: arch/*/kernel/efi.c
F: arch/x86/boot/compressed/eboot.[ch]
F: arch/*/include/asm/efi.h F: arch/*/include/asm/efi.h
F: arch/x86/platform/efi/ F: arch/x86/platform/efi/
F: drivers/firmware/efi/ F: drivers/firmware/efi/

View File

@ -60,7 +60,7 @@ optional_header:
.long __pecoff_code_size @ SizeOfCode .long __pecoff_code_size @ SizeOfCode
.long __pecoff_data_size @ SizeOfInitializedData .long __pecoff_data_size @ SizeOfInitializedData
.long 0 @ SizeOfUninitializedData .long 0 @ SizeOfUninitializedData
.long efi_stub_entry - start @ AddressOfEntryPoint .long efi_entry - start @ AddressOfEntryPoint
.long start_offset @ BaseOfCode .long start_offset @ BaseOfCode
.long __pecoff_data_start - start @ BaseOfData .long __pecoff_data_start - start @ BaseOfData
@ -70,8 +70,8 @@ extra_header_fields:
.long SZ_512 @ FileAlignment .long SZ_512 @ FileAlignment
.short 0 @ MajorOsVersion .short 0 @ MajorOsVersion
.short 0 @ MinorOsVersion .short 0 @ MinorOsVersion
.short 0 @ MajorImageVersion .short LINUX_EFISTUB_MAJOR_VERSION @ MajorImageVersion
.short 0 @ MinorImageVersion .short LINUX_EFISTUB_MINOR_VERSION @ MinorImageVersion
.short 0 @ MajorSubsystemVersion .short 0 @ MajorSubsystemVersion
.short 0 @ MinorSubsystemVersion .short 0 @ MinorSubsystemVersion
.long 0 @ Win32VersionValue .long 0 @ Win32VersionValue

View File

@ -1437,29 +1437,25 @@ __enter_kernel:
reloc_code_end: reloc_code_end:
#ifdef CONFIG_EFI_STUB #ifdef CONFIG_EFI_STUB
.align 2 ENTRY(efi_enter_kernel)
_start: .long start - . mov r7, r0 @ preserve image base
mov r4, r1 @ preserve DT pointer
ENTRY(efi_stub_entry) mov r0, r4 @ DT start
@ allocate space on stack for passing current zImage address add r1, r4, r2 @ DT end
@ and for the EFI stub to return of new entry point of bl cache_clean_flush
@ zImage, as EFI stub may copy the kernel. Pointer address
@ is passed in r2. r0 and r1 are passed through from the
@ EFI firmware to efi_entry
adr ip, _start
ldr r3, [ip]
add r3, r3, ip
stmfd sp!, {r3, lr}
mov r2, sp @ pass zImage address in r2
bl efi_entry
@ Check for error return from EFI stub. r0 has FDT address mov r0, r7 @ relocated zImage
@ or error code. ldr r1, =_edata @ size of zImage
cmn r0, #1 add r1, r1, r0 @ end of zImage
beq efi_load_fail bl cache_clean_flush
@ Preserve return value of efi_entry() in r4 @ The PE/COFF loader might not have cleaned the code we are
mov r4, r0 @ running beyond the PoU, and so calling cache_off below from
@ inside the PE/COFF loader allocated region is unsafe unless
@ we explicitly clean it to the PoC.
adr r0, call_cache_fn @ region of code we will
adr r1, 0f @ run with MMU off
bl cache_clean_flush bl cache_clean_flush
bl cache_off bl cache_off
@ -1469,18 +1465,10 @@ ENTRY(efi_stub_entry)
mov r0, #0 mov r0, #0
mov r1, #0xFFFFFFFF mov r1, #0xFFFFFFFF
mov r2, r4 mov r2, r4
add r7, r7, #(__efi_start - start)
@ Branch to (possibly) relocated zImage that is in [sp] mov pc, r7 @ no mode switch
ldr lr, [sp] ENDPROC(efi_enter_kernel)
ldr ip, =start_offset 0:
add lr, lr, ip
mov pc, lr @ no mode switch
efi_load_fail:
@ Return EFI_LOAD_ERROR to EFI firmware on error.
ldr r0, =0x80000001
ldmfd sp!, {ip, pc}
ENDPROC(efi_stub_entry)
#endif #endif
.align .align

View File

@ -57,13 +57,6 @@ efi_status_t __efi_rt_asm_wrapper(void *, const char *, ...);
/* arch specific definitions used by the stub code */ /* arch specific definitions used by the stub code */
/*
* AArch64 requires the DTB to be 8-byte aligned in the first 512MiB from
* start of kernel and may not cross a 2MiB boundary. We set alignment to
* 2MiB so we know it won't cross a 2MiB boundary.
*/
#define EFI_FDT_ALIGN SZ_2M /* used by allocate_new_fdt_and_exit_boot() */
/* /*
* In some configurations (e.g. VMAP_STACK && 64K pages), stacks built into the * In some configurations (e.g. VMAP_STACK && 64K pages), stacks built into the
* kernel need greater alignment than we require the segments to be padded to. * kernel need greater alignment than we require the segments to be padded to.
@ -107,9 +100,6 @@ static inline void free_screen_info(struct screen_info *si)
{ {
} }
/* redeclare as 'hidden' so the compiler will generate relative references */
extern struct screen_info screen_info __attribute__((__visibility__("hidden")));
static inline void efifb_setup_from_dmi(struct screen_info *si, const char *opt) static inline void efifb_setup_from_dmi(struct screen_info *si, const char *opt)
{ {
} }

View File

@ -10,81 +10,35 @@
#include <asm/assembler.h> #include <asm/assembler.h>
#define EFI_LOAD_ERROR 0x8000000000000001
__INIT __INIT
/* SYM_CODE_START(efi_enter_kernel)
* We arrive here from the EFI boot manager with:
*
* * CPU in little-endian mode
* * MMU on with identity-mapped RAM
* * Icache and Dcache on
*
* We will most likely be running from some place other than where
* we want to be. The kernel image wants to be placed at TEXT_OFFSET
* from start of RAM.
*/
ENTRY(entry)
/*
* Create a stack frame to save FP/LR with extra space
* for image_addr variable passed to efi_entry().
*/
stp x29, x30, [sp, #-32]!
mov x29, sp
/*
* Call efi_entry to do the real work.
* x0 and x1 are already set up by firmware. Current runtime
* address of image is calculated and passed via *image_addr.
*
* unsigned long efi_entry(void *handle,
* efi_system_table_t *sys_table,
* unsigned long *image_addr) ;
*/
adr_l x8, _text
add x2, sp, 16
str x8, [x2]
bl efi_entry
cmn x0, #1
b.eq efi_load_fail
/* /*
* efi_entry() will have copied the kernel image if necessary and we * efi_entry() will have copied the kernel image if necessary and we
* return here with device tree address in x0 and the kernel entry * end up here with device tree address in x1 and the kernel entry
* point stored at *image_addr. Save those values in registers which * point stored in x0. Save those values in registers which are
* are callee preserved. * callee preserved.
*/ */
mov x20, x0 // DTB address ldr w2, =stext_offset
ldr x0, [sp, #16] // relocated _text address add x19, x0, x2 // relocated Image entrypoint
ldr w21, =stext_offset mov x20, x1 // DTB address
add x21, x0, x21
/* /*
* Calculate size of the kernel Image (same for original and copy). * Clean the copied Image to the PoC, and ensure it is not shadowed by
*/
adr_l x1, _text
adr_l x2, _edata
sub x1, x2, x1
/*
* Flush the copied Image to the PoC, and ensure it is not shadowed by
* stale icache entries from before relocation. * stale icache entries from before relocation.
*/ */
bl __flush_dcache_area ldr w1, =kernel_size
bl __clean_dcache_area_poc
ic ialluis ic ialluis
/* /*
* Ensure that the rest of this function (in the original Image) is * Clean the remainder of this routine to the PoC
* visible when the caches are disabled. The I-cache can't have stale * so that we can safely disable the MMU and caches.
* entries for the VA range of the current image, so no maintenance is
* necessary.
*/ */
adr x0, entry adr x0, 0f
adr x1, entry_end ldr w1, 3f
sub x1, x1, x0 bl __clean_dcache_area_poc
bl __flush_dcache_area 0:
/* Turn off Dcache and MMU */ /* Turn off Dcache and MMU */
mrs x0, CurrentEL mrs x0, CurrentEL
cmp x0, #CurrentEL_EL2 cmp x0, #CurrentEL_EL2
@ -109,12 +63,6 @@ ENTRY(entry)
mov x1, xzr mov x1, xzr
mov x2, xzr mov x2, xzr
mov x3, xzr mov x3, xzr
br x21 br x19
SYM_CODE_END(efi_enter_kernel)
efi_load_fail: 3: .long . - 0b
mov x0, #EFI_LOAD_ERROR
ldp x29, x30, [sp], #32
ret
entry_end:
ENDPROC(entry)

View File

@ -27,7 +27,7 @@ optional_header:
.long __initdata_begin - efi_header_end // SizeOfCode .long __initdata_begin - efi_header_end // SizeOfCode
.long __pecoff_data_size // SizeOfInitializedData .long __pecoff_data_size // SizeOfInitializedData
.long 0 // SizeOfUninitializedData .long 0 // SizeOfUninitializedData
.long __efistub_entry - _head // AddressOfEntryPoint .long __efistub_efi_entry - _head // AddressOfEntryPoint
.long efi_header_end - _head // BaseOfCode .long efi_header_end - _head // BaseOfCode
extra_header_fields: extra_header_fields:
@ -36,8 +36,8 @@ extra_header_fields:
.long PECOFF_FILE_ALIGNMENT // FileAlignment .long PECOFF_FILE_ALIGNMENT // FileAlignment
.short 0 // MajorOperatingSystemVersion .short 0 // MajorOperatingSystemVersion
.short 0 // MinorOperatingSystemVersion .short 0 // MinorOperatingSystemVersion
.short 0 // MajorImageVersion .short LINUX_EFISTUB_MAJOR_VERSION // MajorImageVersion
.short 0 // MinorImageVersion .short LINUX_EFISTUB_MINOR_VERSION // MinorImageVersion
.short 0 // MajorSubsystemVersion .short 0 // MajorSubsystemVersion
.short 0 // MinorSubsystemVersion .short 0 // MinorSubsystemVersion
.long 0 // Win32VersionValue .long 0 // Win32VersionValue

View File

@ -12,7 +12,9 @@
#ifdef CONFIG_EFI #ifdef CONFIG_EFI
__efistub_stext_offset = stext - _text; __efistub_kernel_size = _edata - _text;
__efistub_stext_offset = stext - _text;
/* /*
* The EFI stub has its own symbol namespace prefixed by __efistub_, to * The EFI stub has its own symbol namespace prefixed by __efistub_, to
@ -33,7 +35,7 @@ __efistub_strnlen = __pi_strnlen;
__efistub_strcmp = __pi_strcmp; __efistub_strcmp = __pi_strcmp;
__efistub_strncmp = __pi_strncmp; __efistub_strncmp = __pi_strncmp;
__efistub_strrchr = __pi_strrchr; __efistub_strrchr = __pi_strrchr;
__efistub___flush_dcache_area = __pi___flush_dcache_area; __efistub___clean_dcache_area_poc = __pi___clean_dcache_area_poc;
#ifdef CONFIG_KASAN #ifdef CONFIG_KASAN
__efistub___memcpy = __pi_memcpy; __efistub___memcpy = __pi_memcpy;
@ -45,6 +47,7 @@ __efistub__text = _text;
__efistub__end = _end; __efistub__end = _end;
__efistub__edata = _edata; __efistub__edata = _edata;
__efistub_screen_info = screen_info; __efistub_screen_info = screen_info;
__efistub__ctype = _ctype;
#endif #endif

View File

@ -45,11 +45,21 @@
#define EFI_DEBUG 0 #define EFI_DEBUG 0
#define ESI_TABLE_GUID \
EFI_GUID(0x43EA58DC, 0xCF28, 0x4b06, 0xB3, \
0x91, 0xB7, 0x50, 0x59, 0x34, 0x2B, 0xD4)
static unsigned long mps_phys = EFI_INVALID_TABLE_ADDR;
static __initdata unsigned long palo_phys; static __initdata unsigned long palo_phys;
unsigned long __initdata esi_phys = EFI_INVALID_TABLE_ADDR;
unsigned long hcdp_phys = EFI_INVALID_TABLE_ADDR;
unsigned long sal_systab_phys = EFI_INVALID_TABLE_ADDR; unsigned long sal_systab_phys = EFI_INVALID_TABLE_ADDR;
static __initdata efi_config_table_type_t arch_tables[] = { static const efi_config_table_type_t arch_tables[] __initconst = {
{ESI_TABLE_GUID, "ESI", &esi_phys},
{HCDP_TABLE_GUID, "HCDP", &hcdp_phys},
{MPS_TABLE_GUID, "MPS", &mps_phys},
{PROCESSOR_ABSTRACTION_LAYER_OVERWRITE_GUID, "PALO", &palo_phys}, {PROCESSOR_ABSTRACTION_LAYER_OVERWRITE_GUID, "PALO", &palo_phys},
{SAL_SYSTEM_TABLE_GUID, "SALsystab", &sal_systab_phys}, {SAL_SYSTEM_TABLE_GUID, "SALsystab", &sal_systab_phys},
{NULL_GUID, NULL, 0}, {NULL_GUID, NULL, 0},
@ -474,11 +484,10 @@ efi_map_pal_code (void)
void __init void __init
efi_init (void) efi_init (void)
{ {
const efi_system_table_t *efi_systab;
void *efi_map_start, *efi_map_end; void *efi_map_start, *efi_map_end;
efi_char16_t *c16;
u64 efi_desc_size; u64 efi_desc_size;
char *cp, vendor[100] = "unknown"; char *cp;
int i;
set_bit(EFI_BOOT, &efi.flags); set_bit(EFI_BOOT, &efi.flags);
set_bit(EFI_64BIT, &efi.flags); set_bit(EFI_64BIT, &efi.flags);
@ -508,42 +517,29 @@ efi_init (void)
printk(KERN_INFO "Ignoring memory above %lluMB\n", printk(KERN_INFO "Ignoring memory above %lluMB\n",
max_addr >> 20); max_addr >> 20);
efi.systab = __va(ia64_boot_param->efi_systab); efi_systab = __va(ia64_boot_param->efi_systab);
/* /*
* Verify the EFI Table * Verify the EFI Table
*/ */
if (efi.systab == NULL) if (efi_systab == NULL)
panic("Whoa! Can't find EFI system table.\n"); panic("Whoa! Can't find EFI system table.\n");
if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) if (efi_systab_check_header(&efi_systab->hdr, 1))
panic("Whoa! EFI system table signature incorrect\n"); panic("Whoa! EFI system table signature incorrect\n");
if ((efi.systab->hdr.revision >> 16) == 0)
printk(KERN_WARNING "Warning: EFI system table version "
"%d.%02d, expected 1.00 or greater\n",
efi.systab->hdr.revision >> 16,
efi.systab->hdr.revision & 0xffff);
/* Show what we know for posterity */ efi_systab_report_header(&efi_systab->hdr, efi_systab->fw_vendor);
c16 = __va(efi.systab->fw_vendor);
if (c16) {
for (i = 0;i < (int) sizeof(vendor) - 1 && *c16; ++i)
vendor[i] = *c16++;
vendor[i] = '\0';
}
printk(KERN_INFO "EFI v%u.%.02u by %s:",
efi.systab->hdr.revision >> 16,
efi.systab->hdr.revision & 0xffff, vendor);
palo_phys = EFI_INVALID_TABLE_ADDR; palo_phys = EFI_INVALID_TABLE_ADDR;
if (efi_config_init(arch_tables) != 0) if (efi_config_parse_tables(__va(efi_systab->tables),
efi_systab->nr_tables,
arch_tables) != 0)
return; return;
if (palo_phys != EFI_INVALID_TABLE_ADDR) if (palo_phys != EFI_INVALID_TABLE_ADDR)
handle_palo(palo_phys); handle_palo(palo_phys);
runtime = __va(efi.systab->runtime); runtime = __va(efi_systab->runtime);
efi.get_time = phys_get_time; efi.get_time = phys_get_time;
efi.set_time = phys_set_time; efi.set_time = phys_set_time;
efi.get_wakeup_time = phys_get_wakeup_time; efi.get_wakeup_time = phys_get_wakeup_time;
@ -1351,3 +1347,12 @@ vmcore_find_descriptor_size (unsigned long address)
return ret; return ret;
} }
#endif #endif
char *efi_systab_show_arch(char *str)
{
if (mps_phys != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "MPS=0x%lx\n", mps_phys);
if (hcdp_phys != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "HCDP=0x%lx\n", hcdp_phys);
return str;
}

View File

@ -19,10 +19,6 @@ MODULE_LICENSE("GPL");
#define MODULE_NAME "esi" #define MODULE_NAME "esi"
#define ESI_TABLE_GUID \
EFI_GUID(0x43EA58DC, 0xCF28, 0x4b06, 0xB3, \
0x91, 0xB7, 0x50, 0x59, 0x34, 0x2B, 0xD4)
enum esi_systab_entry_type { enum esi_systab_entry_type {
ESI_DESC_ENTRY_POINT = 0 ESI_DESC_ENTRY_POINT = 0
}; };
@ -48,27 +44,18 @@ struct pdesc {
static struct ia64_sal_systab *esi_systab; static struct ia64_sal_systab *esi_systab;
extern unsigned long esi_phys;
static int __init esi_init (void) static int __init esi_init (void)
{ {
efi_config_table_t *config_tables;
struct ia64_sal_systab *systab; struct ia64_sal_systab *systab;
unsigned long esi = 0;
char *p; char *p;
int i; int i;
config_tables = __va(efi.systab->tables); if (esi_phys == EFI_INVALID_TABLE_ADDR)
for (i = 0; i < (int) efi.systab->nr_tables; ++i) {
if (efi_guidcmp(config_tables[i].guid, ESI_TABLE_GUID) == 0) {
esi = config_tables[i].table;
break;
}
}
if (!esi)
return -ENODEV; return -ENODEV;
systab = __va(esi); systab = __va(esi_phys);
if (strncmp(systab->signature, "ESIT", 4) != 0) { if (strncmp(systab->signature, "ESIT", 4) != 0) {
printk(KERN_ERR "bad signature in ESI system table!"); printk(KERN_ERR "bad signature in ESI system table!");

View File

@ -88,7 +88,7 @@ $(obj)/vmlinux.bin: $(obj)/compressed/vmlinux FORCE
SETUP_OBJS = $(addprefix $(obj)/,$(setup-y)) SETUP_OBJS = $(addprefix $(obj)/,$(setup-y))
sed-zoffset := -e 's/^\([0-9a-fA-F]*\) [a-zA-Z] \(startup_32\|startup_64\|efi32_stub_entry\|efi64_stub_entry\|efi_pe_entry\|input_data\|kernel_info\|_end\|_ehead\|_text\|z_.*\)$$/\#define ZO_\2 0x\1/p' sed-zoffset := -e 's/^\([0-9a-fA-F]*\) [a-zA-Z] \(startup_32\|startup_64\|efi32_stub_entry\|efi64_stub_entry\|efi_pe_entry\|efi32_pe_entry\|input_data\|kernel_info\|_end\|_ehead\|_text\|z_.*\)$$/\#define ZO_\2 0x\1/p'
quiet_cmd_zoffset = ZOFFSET $@ quiet_cmd_zoffset = ZOFFSET $@
cmd_zoffset = $(NM) $< | sed -n $(sed-zoffset) > $@ cmd_zoffset = $(NM) $< | sed -n $(sed-zoffset) > $@

View File

@ -87,10 +87,7 @@ endif
vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o
$(obj)/eboot.o: KBUILD_CFLAGS += -fshort-wchar -mno-red-zone vmlinux-objs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a
vmlinux-objs-$(CONFIG_EFI_STUB) += $(obj)/eboot.o \
$(objtree)/drivers/firmware/efi/libstub/lib.a
vmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_thunk_$(BITS).o vmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_thunk_$(BITS).o
# The compressed kernel is built with -fPIC/-fPIE so that a boot loader # The compressed kernel is built with -fPIC/-fPIE so that a boot loader

View File

@ -1,31 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef BOOT_COMPRESSED_EBOOT_H
#define BOOT_COMPRESSED_EBOOT_H
#define SEG_TYPE_DATA (0 << 3)
#define SEG_TYPE_READ_WRITE (1 << 1)
#define SEG_TYPE_CODE (1 << 3)
#define SEG_TYPE_EXEC_READ (1 << 1)
#define SEG_TYPE_TSS ((1 << 3) | (1 << 0))
#define SEG_OP_SIZE_32BIT (1 << 0)
#define SEG_GRANULARITY_4KB (1 << 0)
#define DESC_TYPE_CODE_DATA (1 << 0)
typedef union efi_uga_draw_protocol efi_uga_draw_protocol_t;
union efi_uga_draw_protocol {
struct {
efi_status_t (__efiapi *get_mode)(efi_uga_draw_protocol_t *,
u32*, u32*, u32*, u32*);
void *set_mode;
void *blt;
};
struct {
u32 get_mode;
u32 set_mode;
u32 blt;
} mixed_mode;
};
#endif /* BOOT_COMPRESSED_EBOOT_H */

View File

@ -54,11 +54,16 @@ SYM_FUNC_START(__efi64_thunk)
* Switch to gdt with 32-bit segments. This is the firmware GDT * Switch to gdt with 32-bit segments. This is the firmware GDT
* that was installed when the kernel started executing. This * that was installed when the kernel started executing. This
* pointer was saved at the EFI stub entry point in head_64.S. * pointer was saved at the EFI stub entry point in head_64.S.
*
* Pass the saved DS selector to the 32-bit code, and use far return to
* restore the saved CS selector.
*/ */
leaq efi32_boot_gdt(%rip), %rax leaq efi32_boot_gdt(%rip), %rax
lgdt (%rax) lgdt (%rax)
pushq $__KERNEL_CS movzwl efi32_boot_ds(%rip), %edx
movzwq efi32_boot_cs(%rip), %rax
pushq %rax
leaq efi_enter32(%rip), %rax leaq efi_enter32(%rip), %rax
pushq %rax pushq %rax
lretq lretq
@ -73,6 +78,10 @@ SYM_FUNC_START(__efi64_thunk)
movl %ebx, %es movl %ebx, %es
pop %rbx pop %rbx
movl %ebx, %ds movl %ebx, %ds
/* Clear out 32-bit selector from FS and GS */
xorl %ebx, %ebx
movl %ebx, %fs
movl %ebx, %gs
/* /*
* Convert 32-bit status code into 64-bit. * Convert 32-bit status code into 64-bit.
@ -92,10 +101,12 @@ SYM_FUNC_END(__efi64_thunk)
* The stack should represent the 32-bit calling convention. * The stack should represent the 32-bit calling convention.
*/ */
SYM_FUNC_START_LOCAL(efi_enter32) SYM_FUNC_START_LOCAL(efi_enter32)
movl $__KERNEL_DS, %eax /* Load firmware selector into data and stack segment registers */
movl %eax, %ds movl %edx, %ds
movl %eax, %es movl %edx, %es
movl %eax, %ss movl %edx, %fs
movl %edx, %gs
movl %edx, %ss
/* Reload pgtables */ /* Reload pgtables */
movl %cr3, %eax movl %cr3, %eax
@ -157,6 +168,14 @@ SYM_DATA_START(efi32_boot_gdt)
.quad 0 .quad 0
SYM_DATA_END(efi32_boot_gdt) SYM_DATA_END(efi32_boot_gdt)
SYM_DATA_START(efi32_boot_cs)
.word 0
SYM_DATA_END(efi32_boot_cs)
SYM_DATA_START(efi32_boot_ds)
.word 0
SYM_DATA_END(efi32_boot_ds)
SYM_DATA_START(efi_gdt64) SYM_DATA_START(efi_gdt64)
.word efi_gdt64_end - efi_gdt64 .word efi_gdt64_end - efi_gdt64
.long 0 /* Filled out by user */ .long 0 /* Filled out by user */

View File

@ -63,21 +63,7 @@
__HEAD __HEAD
SYM_FUNC_START(startup_32) SYM_FUNC_START(startup_32)
cld cld
/*
* Test KEEP_SEGMENTS flag to see if the bootloader is asking
* us to not reload segments
*/
testb $KEEP_SEGMENTS, BP_loadflags(%esi)
jnz 1f
cli cli
movl $__BOOT_DS, %eax
movl %eax, %ds
movl %eax, %es
movl %eax, %fs
movl %eax, %gs
movl %eax, %ss
1:
/* /*
* Calculate the delta between where we were compiled to run * Calculate the delta between where we were compiled to run
@ -89,32 +75,59 @@ SYM_FUNC_START(startup_32)
*/ */
leal (BP_scratch+4)(%esi), %esp leal (BP_scratch+4)(%esi), %esp
call 1f call 1f
1: popl %ebp 1: popl %edx
subl $1b, %ebp subl $1b, %edx
/* Load new GDT */
leal gdt(%edx), %eax
movl %eax, 2(%eax)
lgdt (%eax)
/* Load segment registers with our descriptors */
movl $__BOOT_DS, %eax
movl %eax, %ds
movl %eax, %es
movl %eax, %fs
movl %eax, %gs
movl %eax, %ss
/* /*
* %ebp contains the address we are loaded at by the boot loader and %ebx * %edx contains the address we are loaded at by the boot loader and %ebx
* contains the address where we should move the kernel image temporarily * contains the address where we should move the kernel image temporarily
* for safe in-place decompression. * for safe in-place decompression. %ebp contains the address that the kernel
* will be decompressed to.
*/ */
#ifdef CONFIG_RELOCATABLE #ifdef CONFIG_RELOCATABLE
movl %ebp, %ebx movl %edx, %ebx
#ifdef CONFIG_EFI_STUB
/*
* If we were loaded via the EFI LoadImage service, startup_32() will be at an
* offset to the start of the space allocated for the image. efi_pe_entry() will
* set up image_offset to tell us where the image actually starts, so that we
* can use the full available buffer.
* image_offset = startup_32 - image_base
* Otherwise image_offset will be zero and has no effect on the calculations.
*/
subl image_offset(%edx), %ebx
#endif
movl BP_kernel_alignment(%esi), %eax movl BP_kernel_alignment(%esi), %eax
decl %eax decl %eax
addl %eax, %ebx addl %eax, %ebx
notl %eax notl %eax
andl %eax, %ebx andl %eax, %ebx
cmpl $LOAD_PHYSICAL_ADDR, %ebx cmpl $LOAD_PHYSICAL_ADDR, %ebx
jge 1f jae 1f
#endif #endif
movl $LOAD_PHYSICAL_ADDR, %ebx movl $LOAD_PHYSICAL_ADDR, %ebx
1: 1:
movl %ebx, %ebp // Save the output address for later
/* Target address to relocate to for decompression */ /* Target address to relocate to for decompression */
movl BP_init_size(%esi), %eax addl BP_init_size(%esi), %ebx
subl $_end, %eax subl $_end, %ebx
addl %eax, %ebx
/* Set up the stack */ /* Set up the stack */
leal boot_stack_end(%ebx), %esp leal boot_stack_end(%ebx), %esp
@ -128,7 +141,7 @@ SYM_FUNC_START(startup_32)
* where decompression in place becomes safe. * where decompression in place becomes safe.
*/ */
pushl %esi pushl %esi
leal (_bss-4)(%ebp), %esi leal (_bss-4)(%edx), %esi
leal (_bss-4)(%ebx), %edi leal (_bss-4)(%ebx), %edi
movl $(_bss - startup_32), %ecx movl $(_bss - startup_32), %ecx
shrl $2, %ecx shrl $2, %ecx
@ -137,6 +150,15 @@ SYM_FUNC_START(startup_32)
cld cld
popl %esi popl %esi
/*
* The GDT may get overwritten either during the copy we just did or
* during extract_kernel below. To avoid any issues, repoint the GDTR
* to the new copy of the GDT.
*/
leal gdt(%ebx), %eax
movl %eax, 2(%eax)
lgdt (%eax)
/* /*
* Jump to the relocated address. * Jump to the relocated address.
*/ */
@ -148,9 +170,8 @@ SYM_FUNC_END(startup_32)
SYM_FUNC_START(efi32_stub_entry) SYM_FUNC_START(efi32_stub_entry)
SYM_FUNC_START_ALIAS(efi_stub_entry) SYM_FUNC_START_ALIAS(efi_stub_entry)
add $0x4, %esp add $0x4, %esp
movl 8(%esp), %esi /* save boot_params pointer */
call efi_main call efi_main
movl %eax, %esi
movl BP_code32_start(%esi), %eax
leal startup_32(%eax), %eax leal startup_32(%eax), %eax
jmp *%eax jmp *%eax
SYM_FUNC_END(efi32_stub_entry) SYM_FUNC_END(efi32_stub_entry)
@ -189,9 +210,7 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
/* push arguments for extract_kernel: */ /* push arguments for extract_kernel: */
pushl $z_output_len /* decompressed length, end of relocs */ pushl $z_output_len /* decompressed length, end of relocs */
leal _end(%ebx), %eax pushl %ebp /* output address */
subl BP_init_size(%esi), %eax
pushl %eax /* output address */
pushl $z_input_len /* input_len */ pushl $z_input_len /* input_len */
leal input_data(%ebx), %eax leal input_data(%ebx), %eax
@ -209,6 +228,21 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
jmp *%eax jmp *%eax
SYM_FUNC_END(.Lrelocated) SYM_FUNC_END(.Lrelocated)
.data
.balign 8
SYM_DATA_START_LOCAL(gdt)
.word gdt_end - gdt - 1
.long 0
.word 0
.quad 0x0000000000000000 /* Reserved */
.quad 0x00cf9a000000ffff /* __KERNEL_CS */
.quad 0x00cf92000000ffff /* __KERNEL_DS */
SYM_DATA_END_LABEL(gdt, SYM_L_LOCAL, gdt_end)
#ifdef CONFIG_EFI_STUB
SYM_DATA(image_offset, .long 0)
#endif
/* /*
* Stack and heap for uncompression * Stack and heap for uncompression
*/ */

View File

@ -53,19 +53,7 @@ SYM_FUNC_START(startup_32)
* all need to be under the 4G limit. * all need to be under the 4G limit.
*/ */
cld cld
/*
* Test KEEP_SEGMENTS flag to see if the bootloader is asking
* us to not reload segments
*/
testb $KEEP_SEGMENTS, BP_loadflags(%esi)
jnz 1f
cli cli
movl $(__BOOT_DS), %eax
movl %eax, %ds
movl %eax, %es
movl %eax, %ss
1:
/* /*
* Calculate the delta between where we were compiled to run * Calculate the delta between where we were compiled to run
@ -80,10 +68,21 @@ SYM_FUNC_START(startup_32)
1: popl %ebp 1: popl %ebp
subl $1b, %ebp subl $1b, %ebp
/* Load new GDT with the 64bit segments using 32bit descriptor */
leal gdt(%ebp), %eax
movl %eax, 2(%eax)
lgdt (%eax)
/* Load segment registers with our descriptors */
movl $__BOOT_DS, %eax
movl %eax, %ds
movl %eax, %es
movl %eax, %fs
movl %eax, %gs
movl %eax, %ss
/* setup a stack and make sure cpu supports long mode. */ /* setup a stack and make sure cpu supports long mode. */
movl $boot_stack_end, %eax leal boot_stack_end(%ebp), %esp
addl %ebp, %eax
movl %eax, %esp
call verify_cpu call verify_cpu
testl %eax, %eax testl %eax, %eax
@ -100,30 +99,38 @@ SYM_FUNC_START(startup_32)
#ifdef CONFIG_RELOCATABLE #ifdef CONFIG_RELOCATABLE
movl %ebp, %ebx movl %ebp, %ebx
#ifdef CONFIG_EFI_STUB
/*
* If we were loaded via the EFI LoadImage service, startup_32 will be at an
* offset to the start of the space allocated for the image. efi_pe_entry will
* set up image_offset to tell us where the image actually starts, so that we
* can use the full available buffer.
* image_offset = startup_32 - image_base
* Otherwise image_offset will be zero and has no effect on the calculations.
*/
subl image_offset(%ebp), %ebx
#endif
movl BP_kernel_alignment(%esi), %eax movl BP_kernel_alignment(%esi), %eax
decl %eax decl %eax
addl %eax, %ebx addl %eax, %ebx
notl %eax notl %eax
andl %eax, %ebx andl %eax, %ebx
cmpl $LOAD_PHYSICAL_ADDR, %ebx cmpl $LOAD_PHYSICAL_ADDR, %ebx
jge 1f jae 1f
#endif #endif
movl $LOAD_PHYSICAL_ADDR, %ebx movl $LOAD_PHYSICAL_ADDR, %ebx
1: 1:
/* Target address to relocate to for decompression */ /* Target address to relocate to for decompression */
movl BP_init_size(%esi), %eax addl BP_init_size(%esi), %ebx
subl $_end, %eax subl $_end, %ebx
addl %eax, %ebx
/* /*
* Prepare for entering 64 bit mode * Prepare for entering 64 bit mode
*/ */
/* Load new GDT with the 64bit segments using 32bit descriptor */
addl %ebp, gdt+2(%ebp)
lgdt gdt(%ebp)
/* Enable PAE mode */ /* Enable PAE mode */
movl %cr4, %eax movl %cr4, %eax
orl $X86_CR4_PAE, %eax orl $X86_CR4_PAE, %eax
@ -212,8 +219,13 @@ SYM_FUNC_START(startup_32)
cmp $0, %edi cmp $0, %edi
jz 1f jz 1f
leal efi64_stub_entry(%ebp), %eax leal efi64_stub_entry(%ebp), %eax
movl %esi, %edx
movl efi32_boot_args+4(%ebp), %esi movl efi32_boot_args+4(%ebp), %esi
movl efi32_boot_args+8(%ebp), %edx // saved bootparams pointer
cmpl $0, %edx
jnz 1f
leal efi_pe_entry(%ebp), %eax
movl %edi, %ecx // MS calling convention
movl %esi, %edx
1: 1:
#endif #endif
pushl %eax pushl %eax
@ -238,11 +250,17 @@ SYM_FUNC_START(efi32_stub_entry)
1: pop %ebp 1: pop %ebp
subl $1b, %ebp subl $1b, %ebp
movl %esi, efi32_boot_args+8(%ebp)
SYM_INNER_LABEL(efi32_pe_stub_entry, SYM_L_LOCAL)
movl %ecx, efi32_boot_args(%ebp) movl %ecx, efi32_boot_args(%ebp)
movl %edx, efi32_boot_args+4(%ebp) movl %edx, efi32_boot_args+4(%ebp)
sgdtl efi32_boot_gdt(%ebp)
movb $0, efi_is64(%ebp) movb $0, efi_is64(%ebp)
/* Save firmware GDTR and code/data selectors */
sgdtl efi32_boot_gdt(%ebp)
movw %cs, efi32_boot_cs(%ebp)
movw %ds, efi32_boot_ds(%ebp)
/* Disable paging */ /* Disable paging */
movl %cr0, %eax movl %cr0, %eax
btrl $X86_CR0_PG_BIT, %eax btrl $X86_CR0_PG_BIT, %eax
@ -266,6 +284,9 @@ SYM_CODE_START(startup_64)
* and command line. * and command line.
*/ */
cld
cli
/* Setup data segments. */ /* Setup data segments. */
xorl %eax, %eax xorl %eax, %eax
movl %eax, %ds movl %eax, %ds
@ -290,13 +311,27 @@ SYM_CODE_START(startup_64)
/* Start with the delta to where the kernel will run at. */ /* Start with the delta to where the kernel will run at. */
#ifdef CONFIG_RELOCATABLE #ifdef CONFIG_RELOCATABLE
leaq startup_32(%rip) /* - $startup_32 */, %rbp leaq startup_32(%rip) /* - $startup_32 */, %rbp
#ifdef CONFIG_EFI_STUB
/*
* If we were loaded via the EFI LoadImage service, startup_32 will be at an
* offset to the start of the space allocated for the image. efi_pe_entry will
* set up image_offset to tell us where the image actually starts, so that we
* can use the full available buffer.
* image_offset = startup_32 - image_base
* Otherwise image_offset will be zero and has no effect on the calculations.
*/
movl image_offset(%rip), %eax
subq %rax, %rbp
#endif
movl BP_kernel_alignment(%rsi), %eax movl BP_kernel_alignment(%rsi), %eax
decl %eax decl %eax
addq %rax, %rbp addq %rax, %rbp
notq %rax notq %rax
andq %rax, %rbp andq %rax, %rbp
cmpq $LOAD_PHYSICAL_ADDR, %rbp cmpq $LOAD_PHYSICAL_ADDR, %rbp
jge 1f jae 1f
#endif #endif
movq $LOAD_PHYSICAL_ADDR, %rbp movq $LOAD_PHYSICAL_ADDR, %rbp
1: 1:
@ -354,9 +389,9 @@ SYM_CODE_START(startup_64)
*/ */
/* Make sure we have GDT with 32-bit code segment */ /* Make sure we have GDT with 32-bit code segment */
leaq gdt(%rip), %rax leaq gdt64(%rip), %rax
movq %rax, gdt64+2(%rip) addq %rax, 2(%rax)
lgdt gdt64(%rip) lgdt (%rax)
/* /*
* paging_prepare() sets up the trampoline and checks if we need to * paging_prepare() sets up the trampoline and checks if we need to
@ -441,6 +476,16 @@ trampoline_return:
cld cld
popq %rsi popq %rsi
/*
* The GDT may get overwritten either during the copy we just did or
* during extract_kernel below. To avoid any issues, repoint the GDTR
* to the new copy of the GDT.
*/
leaq gdt64(%rbx), %rax
leaq gdt(%rbx), %rdx
movq %rdx, 2(%rax)
lgdt (%rax)
/* /*
* Jump to the relocated address. * Jump to the relocated address.
*/ */
@ -453,9 +498,9 @@ SYM_CODE_END(startup_64)
SYM_FUNC_START(efi64_stub_entry) SYM_FUNC_START(efi64_stub_entry)
SYM_FUNC_START_ALIAS(efi_stub_entry) SYM_FUNC_START_ALIAS(efi_stub_entry)
and $~0xf, %rsp /* realign the stack */ and $~0xf, %rsp /* realign the stack */
movq %rdx, %rbx /* save boot_params pointer */
call efi_main call efi_main
movq %rax,%rsi movq %rbx,%rsi
movl BP_code32_start(%esi), %eax
leaq startup_64(%rax), %rax leaq startup_64(%rax), %rax
jmp *%rax jmp *%rax
SYM_FUNC_END(efi64_stub_entry) SYM_FUNC_END(efi64_stub_entry)
@ -613,13 +658,13 @@ SYM_FUNC_END(.Lno_longmode)
.data .data
SYM_DATA_START_LOCAL(gdt64) SYM_DATA_START_LOCAL(gdt64)
.word gdt_end - gdt .word gdt_end - gdt - 1
.quad 0 .quad gdt - gdt64
SYM_DATA_END(gdt64) SYM_DATA_END(gdt64)
.balign 8 .balign 8
SYM_DATA_START_LOCAL(gdt) SYM_DATA_START_LOCAL(gdt)
.word gdt_end - gdt .word gdt_end - gdt - 1
.long gdt .long 0
.word 0 .word 0
.quad 0x00cf9a000000ffff /* __KERNEL32_CS */ .quad 0x00cf9a000000ffff /* __KERNEL32_CS */
.quad 0x00af9a000000ffff /* __KERNEL_CS */ .quad 0x00af9a000000ffff /* __KERNEL_CS */
@ -628,9 +673,97 @@ SYM_DATA_START_LOCAL(gdt)
.quad 0x0000000000000000 /* TS continued */ .quad 0x0000000000000000 /* TS continued */
SYM_DATA_END_LABEL(gdt, SYM_L_LOCAL, gdt_end) SYM_DATA_END_LABEL(gdt, SYM_L_LOCAL, gdt_end)
#ifdef CONFIG_EFI_STUB
SYM_DATA(image_offset, .long 0)
#endif
#ifdef CONFIG_EFI_MIXED #ifdef CONFIG_EFI_MIXED
SYM_DATA_LOCAL(efi32_boot_args, .long 0, 0) SYM_DATA_LOCAL(efi32_boot_args, .long 0, 0, 0)
SYM_DATA(efi_is64, .byte 1) SYM_DATA(efi_is64, .byte 1)
#define ST32_boottime 60 // offsetof(efi_system_table_32_t, boottime)
#define BS32_handle_protocol 88 // offsetof(efi_boot_services_32_t, handle_protocol)
#define LI32_image_base 32 // offsetof(efi_loaded_image_32_t, image_base)
.text
.code32
SYM_FUNC_START(efi32_pe_entry)
/*
* efi_status_t efi32_pe_entry(efi_handle_t image_handle,
* efi_system_table_32_t *sys_table)
*/
pushl %ebp
movl %esp, %ebp
pushl %eax // dummy push to allocate loaded_image
pushl %ebx // save callee-save registers
pushl %edi
call verify_cpu // check for long mode support
testl %eax, %eax
movl $0x80000003, %eax // EFI_UNSUPPORTED
jnz 2f
call 1f
1: pop %ebx
subl $1b, %ebx
/* Get the loaded image protocol pointer from the image handle */
leal -4(%ebp), %eax
pushl %eax // &loaded_image
leal loaded_image_proto(%ebx), %eax
pushl %eax // pass the GUID address
pushl 8(%ebp) // pass the image handle
/*
* Note the alignment of the stack frame.
* sys_table
* handle <-- 16-byte aligned on entry by ABI
* return address
* frame pointer
* loaded_image <-- local variable
* saved %ebx <-- 16-byte aligned here
* saved %edi
* &loaded_image
* &loaded_image_proto
* handle <-- 16-byte aligned for call to handle_protocol
*/
movl 12(%ebp), %eax // sys_table
movl ST32_boottime(%eax), %eax // sys_table->boottime
call *BS32_handle_protocol(%eax) // sys_table->boottime->handle_protocol
addl $12, %esp // restore argument space
testl %eax, %eax
jnz 2f
movl 8(%ebp), %ecx // image_handle
movl 12(%ebp), %edx // sys_table
movl -4(%ebp), %esi // loaded_image
movl LI32_image_base(%esi), %esi // loaded_image->image_base
movl %ebx, %ebp // startup_32 for efi32_pe_stub_entry
/*
* We need to set the image_offset variable here since startup_32() will
* use it before we get to the 64-bit efi_pe_entry() in C code.
*/
subl %esi, %ebx
movl %ebx, image_offset(%ebp) // save image_offset
jmp efi32_pe_stub_entry
2: popl %edi // restore callee-save registers
popl %ebx
leave
ret
SYM_FUNC_END(efi32_pe_entry)
.section ".rodata"
/* EFI loaded image protocol GUID */
.balign 4
SYM_DATA_START_LOCAL(loaded_image_proto)
.long 0x5b1b31a1
.word 0x9562, 0x11d2
.byte 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b
SYM_DATA_END(loaded_image_proto)
#endif #endif
/* /*

View File

@ -15,7 +15,7 @@
* hex while segment addresses are written as segment:offset. * hex while segment addresses are written as segment:offset.
* *
*/ */
#include <linux/pe.h>
#include <asm/segment.h> #include <asm/segment.h>
#include <asm/boot.h> #include <asm/boot.h>
#include <asm/page_types.h> #include <asm/page_types.h>
@ -43,8 +43,7 @@ SYSSEG = 0x1000 /* historical load address >> 4 */
bootsect_start: bootsect_start:
#ifdef CONFIG_EFI_STUB #ifdef CONFIG_EFI_STUB
# "MZ", MS-DOS header # "MZ", MS-DOS header
.byte 0x4d .word MZ_MAGIC
.byte 0x5a
#endif #endif
# Normalize the start address # Normalize the start address
@ -97,39 +96,30 @@ bugger_off_msg:
#ifdef CONFIG_EFI_STUB #ifdef CONFIG_EFI_STUB
pe_header: pe_header:
.ascii "PE" .long PE_MAGIC
.word 0
coff_header: coff_header:
#ifdef CONFIG_X86_32 #ifdef CONFIG_X86_32
.word 0x14c # i386 .set image_file_add_flags, IMAGE_FILE_32BIT_MACHINE
.set pe_opt_magic, PE_OPT_MAGIC_PE32
.word IMAGE_FILE_MACHINE_I386
#else #else
.word 0x8664 # x86-64 .set image_file_add_flags, 0
.set pe_opt_magic, PE_OPT_MAGIC_PE32PLUS
.word IMAGE_FILE_MACHINE_AMD64
#endif #endif
.word 4 # nr_sections .word section_count # nr_sections
.long 0 # TimeDateStamp .long 0 # TimeDateStamp
.long 0 # PointerToSymbolTable .long 0 # PointerToSymbolTable
.long 1 # NumberOfSymbols .long 1 # NumberOfSymbols
.word section_table - optional_header # SizeOfOptionalHeader .word section_table - optional_header # SizeOfOptionalHeader
#ifdef CONFIG_X86_32 .word IMAGE_FILE_EXECUTABLE_IMAGE | \
.word 0x306 # Characteristics. image_file_add_flags | \
# IMAGE_FILE_32BIT_MACHINE | IMAGE_FILE_DEBUG_STRIPPED | \
# IMAGE_FILE_DEBUG_STRIPPED | IMAGE_FILE_LINE_NUMS_STRIPPED # Characteristics
# IMAGE_FILE_EXECUTABLE_IMAGE |
# IMAGE_FILE_LINE_NUMS_STRIPPED
#else
.word 0x206 # Characteristics
# IMAGE_FILE_DEBUG_STRIPPED |
# IMAGE_FILE_EXECUTABLE_IMAGE |
# IMAGE_FILE_LINE_NUMS_STRIPPED
#endif
optional_header: optional_header:
#ifdef CONFIG_X86_32 .word pe_opt_magic
.word 0x10b # PE32 format
#else
.word 0x20b # PE32+ format
#endif
.byte 0x02 # MajorLinkerVersion .byte 0x02 # MajorLinkerVersion
.byte 0x14 # MinorLinkerVersion .byte 0x14 # MinorLinkerVersion
@ -148,17 +138,19 @@ optional_header:
#endif #endif
extra_header_fields: extra_header_fields:
# PE specification requires ImageBase to be 64k aligned
.set image_base, (LOAD_PHYSICAL_ADDR + 0xffff) & ~0xffff
#ifdef CONFIG_X86_32 #ifdef CONFIG_X86_32
.long 0 # ImageBase .long image_base # ImageBase
#else #else
.quad 0 # ImageBase .quad image_base # ImageBase
#endif #endif
.long 0x20 # SectionAlignment .long 0x20 # SectionAlignment
.long 0x20 # FileAlignment .long 0x20 # FileAlignment
.word 0 # MajorOperatingSystemVersion .word 0 # MajorOperatingSystemVersion
.word 0 # MinorOperatingSystemVersion .word 0 # MinorOperatingSystemVersion
.word 0 # MajorImageVersion .word LINUX_EFISTUB_MAJOR_VERSION # MajorImageVersion
.word 0 # MinorImageVersion .word LINUX_EFISTUB_MINOR_VERSION # MinorImageVersion
.word 0 # MajorSubsystemVersion .word 0 # MajorSubsystemVersion
.word 0 # MinorSubsystemVersion .word 0 # MinorSubsystemVersion
.long 0 # Win32VersionValue .long 0 # Win32VersionValue
@ -170,7 +162,7 @@ extra_header_fields:
.long 0x200 # SizeOfHeaders .long 0x200 # SizeOfHeaders
.long 0 # CheckSum .long 0 # CheckSum
.word 0xa # Subsystem (EFI application) .word IMAGE_SUBSYSTEM_EFI_APPLICATION # Subsystem (EFI application)
.word 0 # DllCharacteristics .word 0 # DllCharacteristics
#ifdef CONFIG_X86_32 #ifdef CONFIG_X86_32
.long 0 # SizeOfStackReserve .long 0 # SizeOfStackReserve
@ -184,7 +176,7 @@ extra_header_fields:
.quad 0 # SizeOfHeapCommit .quad 0 # SizeOfHeapCommit
#endif #endif
.long 0 # LoaderFlags .long 0 # LoaderFlags
.long 0x6 # NumberOfRvaAndSizes .long (section_table - .) / 8 # NumberOfRvaAndSizes
.quad 0 # ExportTable .quad 0 # ExportTable
.quad 0 # ImportTable .quad 0 # ImportTable
@ -210,7 +202,10 @@ section_table:
.long 0 # PointerToLineNumbers .long 0 # PointerToLineNumbers
.word 0 # NumberOfRelocations .word 0 # NumberOfRelocations
.word 0 # NumberOfLineNumbers .word 0 # NumberOfLineNumbers
.long 0x60500020 # Characteristics (section flags) .long IMAGE_SCN_CNT_CODE | \
IMAGE_SCN_MEM_READ | \
IMAGE_SCN_MEM_EXECUTE | \
IMAGE_SCN_ALIGN_16BYTES # Characteristics
# #
# The EFI application loader requires a relocation section # The EFI application loader requires a relocation section
@ -228,7 +223,30 @@ section_table:
.long 0 # PointerToLineNumbers .long 0 # PointerToLineNumbers
.word 0 # NumberOfRelocations .word 0 # NumberOfRelocations
.word 0 # NumberOfLineNumbers .word 0 # NumberOfLineNumbers
.long 0x42100040 # Characteristics (section flags) .long IMAGE_SCN_CNT_INITIALIZED_DATA | \
IMAGE_SCN_MEM_READ | \
IMAGE_SCN_MEM_DISCARDABLE | \
IMAGE_SCN_ALIGN_1BYTES # Characteristics
#ifdef CONFIG_EFI_MIXED
#
# The offset & size fields are filled in by build.c.
#
.asciz ".compat"
.long 0
.long 0x0
.long 0 # Size of initialized data
# on disk
.long 0x0
.long 0 # PointerToRelocations
.long 0 # PointerToLineNumbers
.word 0 # NumberOfRelocations
.word 0 # NumberOfLineNumbers
.long IMAGE_SCN_CNT_INITIALIZED_DATA | \
IMAGE_SCN_MEM_READ | \
IMAGE_SCN_MEM_DISCARDABLE | \
IMAGE_SCN_ALIGN_1BYTES # Characteristics
#endif
# #
# The offset & size fields are filled in by build.c. # The offset & size fields are filled in by build.c.
@ -246,27 +264,12 @@ section_table:
.long 0 # PointerToLineNumbers .long 0 # PointerToLineNumbers
.word 0 # NumberOfRelocations .word 0 # NumberOfRelocations
.word 0 # NumberOfLineNumbers .word 0 # NumberOfLineNumbers
.long 0x60500020 # Characteristics (section flags) .long IMAGE_SCN_CNT_CODE | \
IMAGE_SCN_MEM_READ | \
# IMAGE_SCN_MEM_EXECUTE | \
# The offset & size fields are filled in by build.c. IMAGE_SCN_ALIGN_16BYTES # Characteristics
#
.ascii ".bss"
.byte 0
.byte 0
.byte 0
.byte 0
.long 0
.long 0x0
.long 0 # Size of initialized data
# on disk
.long 0x0
.long 0 # PointerToRelocations
.long 0 # PointerToLineNumbers
.word 0 # NumberOfRelocations
.word 0 # NumberOfLineNumbers
.long 0xc8000080 # Characteristics (section flags)
.set section_count, (. - section_table) / 40
#endif /* CONFIG_EFI_STUB */ #endif /* CONFIG_EFI_STUB */
# Kernel attributes; used by setup. This is part 1 of the # Kernel attributes; used by setup. This is part 1 of the

View File

@ -53,11 +53,20 @@ u8 buf[SETUP_SECT_MAX*512];
#define PECOFF_RELOC_RESERVE 0x20 #define PECOFF_RELOC_RESERVE 0x20
#ifdef CONFIG_EFI_MIXED
#define PECOFF_COMPAT_RESERVE 0x20
#else
#define PECOFF_COMPAT_RESERVE 0x0
#endif
unsigned long efi32_stub_entry; unsigned long efi32_stub_entry;
unsigned long efi64_stub_entry; unsigned long efi64_stub_entry;
unsigned long efi_pe_entry; unsigned long efi_pe_entry;
unsigned long efi32_pe_entry;
unsigned long kernel_info; unsigned long kernel_info;
unsigned long startup_64; unsigned long startup_64;
unsigned long _ehead;
unsigned long _end;
/*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/
@ -189,7 +198,10 @@ static void update_pecoff_section_header(char *section_name, u32 offset, u32 siz
static void update_pecoff_setup_and_reloc(unsigned int size) static void update_pecoff_setup_and_reloc(unsigned int size)
{ {
u32 setup_offset = 0x200; u32 setup_offset = 0x200;
u32 reloc_offset = size - PECOFF_RELOC_RESERVE; u32 reloc_offset = size - PECOFF_RELOC_RESERVE - PECOFF_COMPAT_RESERVE;
#ifdef CONFIG_EFI_MIXED
u32 compat_offset = reloc_offset + PECOFF_RELOC_RESERVE;
#endif
u32 setup_size = reloc_offset - setup_offset; u32 setup_size = reloc_offset - setup_offset;
update_pecoff_section_header(".setup", setup_offset, setup_size); update_pecoff_section_header(".setup", setup_offset, setup_size);
@ -201,43 +213,59 @@ static void update_pecoff_setup_and_reloc(unsigned int size)
*/ */
put_unaligned_le32(reloc_offset + 10, &buf[reloc_offset]); put_unaligned_le32(reloc_offset + 10, &buf[reloc_offset]);
put_unaligned_le32(10, &buf[reloc_offset + 4]); put_unaligned_le32(10, &buf[reloc_offset + 4]);
#ifdef CONFIG_EFI_MIXED
update_pecoff_section_header(".compat", compat_offset, PECOFF_COMPAT_RESERVE);
/*
* Put the IA-32 machine type (0x14c) and the associated entry point
* address in the .compat section, so loaders can figure out which other
* execution modes this image supports.
*/
buf[compat_offset] = 0x1;
buf[compat_offset + 1] = 0x8;
put_unaligned_le16(0x14c, &buf[compat_offset + 2]);
put_unaligned_le32(efi32_pe_entry + size, &buf[compat_offset + 4]);
#endif
} }
static void update_pecoff_text(unsigned int text_start, unsigned int file_sz) static void update_pecoff_text(unsigned int text_start, unsigned int file_sz,
unsigned int init_sz)
{ {
unsigned int pe_header; unsigned int pe_header;
unsigned int text_sz = file_sz - text_start; unsigned int text_sz = file_sz - text_start;
unsigned int bss_sz = init_sz - file_sz;
pe_header = get_unaligned_le32(&buf[0x3c]); pe_header = get_unaligned_le32(&buf[0x3c]);
/*
* The PE/COFF loader may load the image at an address which is
* misaligned with respect to the kernel_alignment field in the setup
* header.
*
* In order to avoid relocating the kernel to correct the misalignment,
* add slack to allow the buffer to be aligned within the declared size
* of the image.
*/
bss_sz += CONFIG_PHYSICAL_ALIGN;
init_sz += CONFIG_PHYSICAL_ALIGN;
/* /*
* Size of code: Subtract the size of the first sector (512 bytes) * Size of code: Subtract the size of the first sector (512 bytes)
* which includes the header. * which includes the header.
*/ */
put_unaligned_le32(file_sz - 512, &buf[pe_header + 0x1c]); put_unaligned_le32(file_sz - 512 + bss_sz, &buf[pe_header + 0x1c]);
/* Size of image */
put_unaligned_le32(init_sz, &buf[pe_header + 0x50]);
/* /*
* Address of entry point for PE/COFF executable * Address of entry point for PE/COFF executable
*/ */
put_unaligned_le32(text_start + efi_pe_entry, &buf[pe_header + 0x28]); put_unaligned_le32(text_start + efi_pe_entry, &buf[pe_header + 0x28]);
update_pecoff_section_header(".text", text_start, text_sz); update_pecoff_section_header_fields(".text", text_start, text_sz + bss_sz,
} text_sz, text_start);
static void update_pecoff_bss(unsigned int file_sz, unsigned int init_sz)
{
unsigned int pe_header;
unsigned int bss_sz = init_sz - file_sz;
pe_header = get_unaligned_le32(&buf[0x3c]);
/* Size of uninitialized data */
put_unaligned_le32(bss_sz, &buf[pe_header + 0x24]);
/* Size of image */
put_unaligned_le32(init_sz, &buf[pe_header + 0x50]);
update_pecoff_section_header_fields(".bss", file_sz, bss_sz, 0, 0);
} }
static int reserve_pecoff_reloc_section(int c) static int reserve_pecoff_reloc_section(int c)
@ -278,9 +306,8 @@ static void efi_stub_entry_update(void)
static inline void update_pecoff_setup_and_reloc(unsigned int size) {} static inline void update_pecoff_setup_and_reloc(unsigned int size) {}
static inline void update_pecoff_text(unsigned int text_start, static inline void update_pecoff_text(unsigned int text_start,
unsigned int file_sz) {} unsigned int file_sz,
static inline void update_pecoff_bss(unsigned int file_sz, unsigned int init_sz) {}
unsigned int init_sz) {}
static inline void efi_stub_defaults(void) {} static inline void efi_stub_defaults(void) {}
static inline void efi_stub_entry_update(void) {} static inline void efi_stub_entry_update(void) {}
@ -290,6 +317,12 @@ static inline int reserve_pecoff_reloc_section(int c)
} }
#endif /* CONFIG_EFI_STUB */ #endif /* CONFIG_EFI_STUB */
static int reserve_pecoff_compat_section(int c)
{
/* Reserve 0x20 bytes for .compat section */
memset(buf+c, 0, PECOFF_COMPAT_RESERVE);
return PECOFF_COMPAT_RESERVE;
}
/* /*
* Parse zoffset.h and find the entry points. We could just #include zoffset.h * Parse zoffset.h and find the entry points. We could just #include zoffset.h
@ -322,8 +355,11 @@ static void parse_zoffset(char *fname)
PARSE_ZOFS(p, efi32_stub_entry); PARSE_ZOFS(p, efi32_stub_entry);
PARSE_ZOFS(p, efi64_stub_entry); PARSE_ZOFS(p, efi64_stub_entry);
PARSE_ZOFS(p, efi_pe_entry); PARSE_ZOFS(p, efi_pe_entry);
PARSE_ZOFS(p, efi32_pe_entry);
PARSE_ZOFS(p, kernel_info); PARSE_ZOFS(p, kernel_info);
PARSE_ZOFS(p, startup_64); PARSE_ZOFS(p, startup_64);
PARSE_ZOFS(p, _ehead);
PARSE_ZOFS(p, _end);
p = strchr(p, '\n'); p = strchr(p, '\n');
while (p && (*p == '\r' || *p == '\n')) while (p && (*p == '\r' || *p == '\n'))
@ -365,6 +401,7 @@ int main(int argc, char ** argv)
die("Boot block hasn't got boot flag (0xAA55)"); die("Boot block hasn't got boot flag (0xAA55)");
fclose(file); fclose(file);
c += reserve_pecoff_compat_section(c);
c += reserve_pecoff_reloc_section(c); c += reserve_pecoff_reloc_section(c);
/* Pad unused space with zeros */ /* Pad unused space with zeros */
@ -406,9 +443,28 @@ int main(int argc, char ** argv)
buf[0x1f1] = setup_sectors-1; buf[0x1f1] = setup_sectors-1;
put_unaligned_le32(sys_size, &buf[0x1f4]); put_unaligned_le32(sys_size, &buf[0x1f4]);
update_pecoff_text(setup_sectors * 512, i + (sys_size * 16));
init_sz = get_unaligned_le32(&buf[0x260]); init_sz = get_unaligned_le32(&buf[0x260]);
update_pecoff_bss(i + (sys_size * 16), init_sz); #ifdef CONFIG_EFI_STUB
/*
* The decompression buffer will start at ImageBase. When relocating
* the compressed kernel to its end, we must ensure that the head
* section does not get overwritten. The head section occupies
* [i, i + _ehead), and the destination is [init_sz - _end, init_sz).
*
* At present these should never overlap, because 'i' is at most 32k
* because of SETUP_SECT_MAX, '_ehead' is less than 1k, and the
* calculation of INIT_SIZE in boot/header.S ensures that
* 'init_sz - _end' is at least 64k.
*
* For future-proofing, increase init_sz if necessary.
*/
if (init_sz - _end < i + _ehead) {
init_sz = (i + _ehead + _end + 4095) & ~4095;
put_unaligned_le32(init_sz, &buf[0x260]);
}
#endif
update_pecoff_text(setup_sectors * 512, i + (sys_size * 16), init_sz);
efi_stub_entry_update(); efi_stub_entry_update();

View File

@ -10,6 +10,8 @@
#include <asm/mmu_context.h> #include <asm/mmu_context.h>
#include <linux/build_bug.h> #include <linux/build_bug.h>
extern unsigned long efi_fw_vendor, efi_config_table;
/* /*
* We map the EFI regions needed for runtime services non-contiguously, * We map the EFI regions needed for runtime services non-contiguously,
* with preserved alignment on virtual addresses starting from -4G down * with preserved alignment on virtual addresses starting from -4G down
@ -34,8 +36,6 @@ static inline bool efi_have_uv1_memmap(void)
#define EFI32_LOADER_SIGNATURE "EL32" #define EFI32_LOADER_SIGNATURE "EL32"
#define EFI64_LOADER_SIGNATURE "EL64" #define EFI64_LOADER_SIGNATURE "EL64"
#define MAX_CMDLINE_ADDRESS UINT_MAX
#define ARCH_EFI_IRQ_FLAGS_MASK X86_EFLAGS_IF #define ARCH_EFI_IRQ_FLAGS_MASK X86_EFLAGS_IF
/* /*
@ -180,7 +180,6 @@ extern void __init efi_uv1_memmap_phys_epilog(pgd_t *save_pgd);
struct efi_setup_data { struct efi_setup_data {
u64 fw_vendor; u64 fw_vendor;
u64 runtime;
u64 tables; u64 tables;
u64 smbios; u64 smbios;
u64 reserved[8]; u64 reserved[8];
@ -219,7 +218,8 @@ extern void efi_thunk_runtime_setup(void);
efi_status_t efi_set_virtual_address_map(unsigned long memory_map_size, efi_status_t efi_set_virtual_address_map(unsigned long memory_map_size,
unsigned long descriptor_size, unsigned long descriptor_size,
u32 descriptor_version, u32 descriptor_version,
efi_memory_desc_t *virtual_map); efi_memory_desc_t *virtual_map,
unsigned long systab_phys);
/* arch specific definitions used by the stub code */ /* arch specific definitions used by the stub code */
@ -270,6 +270,11 @@ static inline void *efi64_zero_upper(void *p)
return p; return p;
} }
static inline u32 efi64_convert_status(efi_status_t status)
{
return (u32)(status | (u64)status >> 32);
}
#define __efi64_argmap_free_pages(addr, size) \ #define __efi64_argmap_free_pages(addr, size) \
((addr), 0, (size)) ((addr), 0, (size))
@ -285,11 +290,21 @@ static inline void *efi64_zero_upper(void *p)
#define __efi64_argmap_locate_protocol(protocol, reg, interface) \ #define __efi64_argmap_locate_protocol(protocol, reg, interface) \
((protocol), (reg), efi64_zero_upper(interface)) ((protocol), (reg), efi64_zero_upper(interface))
#define __efi64_argmap_locate_device_path(protocol, path, handle) \
((protocol), (path), efi64_zero_upper(handle))
#define __efi64_argmap_exit(handle, status, size, data) \
((handle), efi64_convert_status(status), (size), (data))
/* PCI I/O */ /* PCI I/O */
#define __efi64_argmap_get_location(protocol, seg, bus, dev, func) \ #define __efi64_argmap_get_location(protocol, seg, bus, dev, func) \
((protocol), efi64_zero_upper(seg), efi64_zero_upper(bus), \ ((protocol), efi64_zero_upper(seg), efi64_zero_upper(bus), \
efi64_zero_upper(dev), efi64_zero_upper(func)) efi64_zero_upper(dev), efi64_zero_upper(func))
/* LoadFile */
#define __efi64_argmap_load_file(protocol, path, policy, bufsize, buf) \
((protocol), (path), (policy), efi64_zero_upper(bufsize), (buf))
/* /*
* The macros below handle the plumbing for the argument mapping. To add a * The macros below handle the plumbing for the argument mapping. To add a
* mapping for a specific EFI method, simply define a macro * mapping for a specific EFI method, simply define a macro

View File

@ -88,7 +88,6 @@ static void __used common(void)
OFFSET(BP_kernel_alignment, boot_params, hdr.kernel_alignment); OFFSET(BP_kernel_alignment, boot_params, hdr.kernel_alignment);
OFFSET(BP_init_size, boot_params, hdr.init_size); OFFSET(BP_init_size, boot_params, hdr.init_size);
OFFSET(BP_pref_address, boot_params, hdr.pref_address); OFFSET(BP_pref_address, boot_params, hdr.pref_address);
OFFSET(BP_code32_start, boot_params, hdr.code32_start);
BLANK(); BLANK();
DEFINE(PTREGS_SIZE, sizeof(struct pt_regs)); DEFINE(PTREGS_SIZE, sizeof(struct pt_regs));

View File

@ -3,6 +3,8 @@
# error "Please do not build this file directly, build asm-offsets.c instead" # error "Please do not build this file directly, build asm-offsets.c instead"
#endif #endif
#include <linux/efi.h>
#include <asm/ucontext.h> #include <asm/ucontext.h>
#define __SYSCALL_I386(nr, sym, qual) [nr] = 1, #define __SYSCALL_I386(nr, sym, qual) [nr] = 1,
@ -64,4 +66,7 @@ void foo(void)
BLANK(); BLANK();
DEFINE(__NR_syscall_max, sizeof(syscalls) - 1); DEFINE(__NR_syscall_max, sizeof(syscalls) - 1);
DEFINE(NR_syscalls, sizeof(syscalls)); DEFINE(NR_syscalls, sizeof(syscalls));
BLANK();
DEFINE(EFI_svam, offsetof(efi_runtime_services_t, set_virtual_address_map));
} }

View File

@ -67,11 +67,6 @@ __HEAD
SYM_CODE_START(startup_32) SYM_CODE_START(startup_32)
movl pa(initial_stack),%ecx movl pa(initial_stack),%ecx
/* test KEEP_SEGMENTS flag to see if the bootloader is asking
us to not reload segments */
testb $KEEP_SEGMENTS, BP_loadflags(%esi)
jnz 2f
/* /*
* Set segments to known values. * Set segments to known values.
*/ */
@ -82,7 +77,6 @@ SYM_CODE_START(startup_32)
movl %eax,%fs movl %eax,%fs
movl %eax,%gs movl %eax,%gs
movl %eax,%ss movl %eax,%ss
2:
leal -__PAGE_OFFSET(%ecx),%esp leal -__PAGE_OFFSET(%ecx),%esp
/* /*

View File

@ -17,7 +17,7 @@ static enum efi_secureboot_mode get_sb_mode(void)
size = sizeof(secboot); size = sizeof(secboot);
if (!efi_enabled(EFI_RUNTIME_SERVICES)) { if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) {
pr_info("ima: secureboot mode unknown, no efi\n"); pr_info("ima: secureboot mode unknown, no efi\n");
return efi_secureboot_mode_unknown; return efi_secureboot_mode_unknown;
} }

View File

@ -141,9 +141,8 @@ prepare_add_efi_setup_data(struct boot_params *params,
struct setup_data *sd = (void *)params + efi_setup_data_offset; struct setup_data *sd = (void *)params + efi_setup_data_offset;
struct efi_setup_data *esd = (void *)sd + sizeof(struct setup_data); struct efi_setup_data *esd = (void *)sd + sizeof(struct setup_data);
esd->fw_vendor = efi.fw_vendor; esd->fw_vendor = efi_fw_vendor;
esd->runtime = efi.runtime; esd->tables = efi_config_table;
esd->tables = efi.config_table;
esd->smbios = efi.smbios; esd->smbios = efi.smbios;
sd->type = SETUP_EFI; sd->type = SETUP_EFI;

View File

@ -54,10 +54,16 @@
#include <asm/x86_init.h> #include <asm/x86_init.h>
#include <asm/uv/uv.h> #include <asm/uv/uv.h>
static efi_system_table_t efi_systab __initdata; static unsigned long efi_systab_phys __initdata;
static u64 efi_systab_phys __initdata; static unsigned long prop_phys = EFI_INVALID_TABLE_ADDR;
static unsigned long uga_phys = EFI_INVALID_TABLE_ADDR;
static unsigned long efi_runtime, efi_nr_tables;
static efi_config_table_type_t arch_tables[] __initdata = { unsigned long efi_fw_vendor, efi_config_table;
static const efi_config_table_type_t arch_tables[] __initconst = {
{EFI_PROPERTIES_TABLE_GUID, "PROP", &prop_phys},
{UGA_IO_PROTOCOL_GUID, "UGA", &uga_phys},
#ifdef CONFIG_X86_UV #ifdef CONFIG_X86_UV
{UV_SYSTEM_TABLE_GUID, "UVsystab", &uv_systab_phys}, {UV_SYSTEM_TABLE_GUID, "UVsystab", &uv_systab_phys},
#endif #endif
@ -65,26 +71,26 @@ static efi_config_table_type_t arch_tables[] __initdata = {
}; };
static const unsigned long * const efi_tables[] = { static const unsigned long * const efi_tables[] = {
&efi.mps,
&efi.acpi, &efi.acpi,
&efi.acpi20, &efi.acpi20,
&efi.smbios, &efi.smbios,
&efi.smbios3, &efi.smbios3,
&efi.boot_info, &uga_phys,
&efi.hcdp,
&efi.uga,
#ifdef CONFIG_X86_UV #ifdef CONFIG_X86_UV
&uv_systab_phys, &uv_systab_phys,
#endif #endif
&efi.fw_vendor, &efi_fw_vendor,
&efi.runtime, &efi_runtime,
&efi.config_table, &efi_config_table,
&efi.esrt, &efi.esrt,
&efi.properties_table, &prop_phys,
&efi.mem_attr_table, &efi_mem_attr_table,
#ifdef CONFIG_EFI_RCI2_TABLE #ifdef CONFIG_EFI_RCI2_TABLE
&rci2_table_phys, &rci2_table_phys,
#endif #endif
&efi.tpm_log,
&efi.tpm_final_log,
&efi_rng_seed,
}; };
u64 efi_setup; /* efi setup_data physical address */ u64 efi_setup; /* efi setup_data physical address */
@ -214,16 +220,13 @@ int __init efi_memblock_x86_reserve_range(void)
if (efi_enabled(EFI_PARAVIRT)) if (efi_enabled(EFI_PARAVIRT))
return 0; return 0;
#ifdef CONFIG_X86_32 /* Can't handle firmware tables above 4GB on i386 */
/* Can't handle data above 4GB at this time */ if (IS_ENABLED(CONFIG_X86_32) && e->efi_memmap_hi > 0) {
if (e->efi_memmap_hi) {
pr_err("Memory map is above 4GB, disabling EFI.\n"); pr_err("Memory map is above 4GB, disabling EFI.\n");
return -EINVAL; return -EINVAL;
} }
pmap = e->efi_memmap; pmap = (phys_addr_t)(e->efi_memmap | ((u64)e->efi_memmap_hi << 32));
#else
pmap = (e->efi_memmap | ((__u64)e->efi_memmap_hi << 32));
#endif
data.phys_map = pmap; data.phys_map = pmap;
data.size = e->efi_memmap_size; data.size = e->efi_memmap_size;
data.desc_size = e->efi_memdesc_size; data.desc_size = e->efi_memdesc_size;
@ -306,11 +309,11 @@ static void __init efi_clean_memmap(void)
if (n_removal > 0) { if (n_removal > 0) {
struct efi_memory_map_data data = { struct efi_memory_map_data data = {
.phys_map = efi.memmap.phys_map, .phys_map = efi.memmap.phys_map,
.desc_version = efi.memmap.desc_version, .desc_version = efi.memmap.desc_version,
.desc_size = efi.memmap.desc_size, .desc_size = efi.memmap.desc_size,
.size = efi.memmap.desc_size * (efi.memmap.nr_map - n_removal), .size = efi.memmap.desc_size * (efi.memmap.nr_map - n_removal),
.flags = 0, .flags = 0,
}; };
pr_warn("Removing %d invalid memory map entries.\n", n_removal); pr_warn("Removing %d invalid memory map entries.\n", n_removal);
@ -334,43 +337,32 @@ void __init efi_print_memmap(void)
} }
} }
static int __init efi_systab_init(u64 phys) static int __init efi_systab_init(unsigned long phys)
{ {
int size = efi_enabled(EFI_64BIT) ? sizeof(efi_system_table_64_t) int size = efi_enabled(EFI_64BIT) ? sizeof(efi_system_table_64_t)
: sizeof(efi_system_table_32_t); : sizeof(efi_system_table_32_t);
const efi_table_hdr_t *hdr;
bool over4g = false; bool over4g = false;
void *p; void *p;
int ret;
p = early_memremap_ro(phys, size); hdr = p = early_memremap_ro(phys, size);
if (p == NULL) { if (p == NULL) {
pr_err("Couldn't map the system table!\n"); pr_err("Couldn't map the system table!\n");
return -ENOMEM; return -ENOMEM;
} }
ret = efi_systab_check_header(hdr, 1);
if (ret) {
early_memunmap(p, size);
return ret;
}
if (efi_enabled(EFI_64BIT)) { if (efi_enabled(EFI_64BIT)) {
const efi_system_table_64_t *systab64 = p; const efi_system_table_64_t *systab64 = p;
efi_systab.hdr = systab64->hdr; efi_runtime = systab64->runtime;
efi_systab.fw_vendor = systab64->fw_vendor; over4g = systab64->runtime > U32_MAX;
efi_systab.fw_revision = systab64->fw_revision;
efi_systab.con_in_handle = systab64->con_in_handle;
efi_systab.con_in = systab64->con_in;
efi_systab.con_out_handle = systab64->con_out_handle;
efi_systab.con_out = (void *)(unsigned long)systab64->con_out;
efi_systab.stderr_handle = systab64->stderr_handle;
efi_systab.stderr = systab64->stderr;
efi_systab.runtime = (void *)(unsigned long)systab64->runtime;
efi_systab.boottime = (void *)(unsigned long)systab64->boottime;
efi_systab.nr_tables = systab64->nr_tables;
efi_systab.tables = systab64->tables;
over4g = systab64->con_in_handle > U32_MAX ||
systab64->con_in > U32_MAX ||
systab64->con_out_handle > U32_MAX ||
systab64->con_out > U32_MAX ||
systab64->stderr_handle > U32_MAX ||
systab64->stderr > U32_MAX ||
systab64->boottime > U32_MAX;
if (efi_setup) { if (efi_setup) {
struct efi_setup_data *data; struct efi_setup_data *data;
@ -381,38 +373,33 @@ static int __init efi_systab_init(u64 phys)
return -ENOMEM; return -ENOMEM;
} }
efi_systab.fw_vendor = (unsigned long)data->fw_vendor; efi_fw_vendor = (unsigned long)data->fw_vendor;
efi_systab.runtime = (void *)(unsigned long)data->runtime; efi_config_table = (unsigned long)data->tables;
efi_systab.tables = (unsigned long)data->tables;
over4g |= data->fw_vendor > U32_MAX || over4g |= data->fw_vendor > U32_MAX ||
data->runtime > U32_MAX ||
data->tables > U32_MAX; data->tables > U32_MAX;
early_memunmap(data, sizeof(*data)); early_memunmap(data, sizeof(*data));
} else { } else {
efi_fw_vendor = systab64->fw_vendor;
efi_config_table = systab64->tables;
over4g |= systab64->fw_vendor > U32_MAX || over4g |= systab64->fw_vendor > U32_MAX ||
systab64->runtime > U32_MAX ||
systab64->tables > U32_MAX; systab64->tables > U32_MAX;
} }
efi_nr_tables = systab64->nr_tables;
} else { } else {
const efi_system_table_32_t *systab32 = p; const efi_system_table_32_t *systab32 = p;
efi_systab.hdr = systab32->hdr; efi_fw_vendor = systab32->fw_vendor;
efi_systab.fw_vendor = systab32->fw_vendor; efi_runtime = systab32->runtime;
efi_systab.fw_revision = systab32->fw_revision; efi_config_table = systab32->tables;
efi_systab.con_in_handle = systab32->con_in_handle; efi_nr_tables = systab32->nr_tables;
efi_systab.con_in = systab32->con_in;
efi_systab.con_out_handle = systab32->con_out_handle;
efi_systab.con_out = (void *)(unsigned long)systab32->con_out;
efi_systab.stderr_handle = systab32->stderr_handle;
efi_systab.stderr = systab32->stderr;
efi_systab.runtime = (void *)(unsigned long)systab32->runtime;
efi_systab.boottime = (void *)(unsigned long)systab32->boottime;
efi_systab.nr_tables = systab32->nr_tables;
efi_systab.tables = systab32->tables;
} }
efi.runtime_version = hdr->revision;
efi_systab_report_header(hdr, efi_fw_vendor);
early_memunmap(p, size); early_memunmap(p, size);
if (IS_ENABLED(CONFIG_X86_32) && over4g) { if (IS_ENABLED(CONFIG_X86_32) && over4g) {
@ -420,29 +407,40 @@ static int __init efi_systab_init(u64 phys)
return -EINVAL; return -EINVAL;
} }
efi.systab = &efi_systab; return 0;
}
static int __init efi_config_init(const efi_config_table_type_t *arch_tables)
{
void *config_tables;
int sz, ret;
if (efi_nr_tables == 0)
return 0;
if (efi_enabled(EFI_64BIT))
sz = sizeof(efi_config_table_64_t);
else
sz = sizeof(efi_config_table_32_t);
/* /*
* Verify the EFI Table * Let's see what config tables the firmware passed to us.
*/ */
if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) { config_tables = early_memremap(efi_config_table, efi_nr_tables * sz);
pr_err("System table signature incorrect!\n"); if (config_tables == NULL) {
return -EINVAL; pr_err("Could not map Configuration table!\n");
return -ENOMEM;
} }
if ((efi.systab->hdr.revision >> 16) == 0)
pr_err("Warning: System table version %d.%02d, expected 1.00 or greater!\n",
efi.systab->hdr.revision >> 16,
efi.systab->hdr.revision & 0xffff);
return 0; ret = efi_config_parse_tables(config_tables, efi_nr_tables,
arch_tables);
early_memunmap(config_tables, efi_nr_tables * sz);
return ret;
} }
void __init efi_init(void) void __init efi_init(void)
{ {
efi_char16_t *c16;
char vendor[100] = "unknown";
int i = 0;
if (IS_ENABLED(CONFIG_X86_32) && if (IS_ENABLED(CONFIG_X86_32) &&
(boot_params.efi_info.efi_systab_hi || (boot_params.efi_info.efi_systab_hi ||
boot_params.efi_info.efi_memmap_hi)) { boot_params.efi_info.efi_memmap_hi)) {
@ -456,29 +454,7 @@ void __init efi_init(void)
if (efi_systab_init(efi_systab_phys)) if (efi_systab_init(efi_systab_phys))
return; return;
efi.config_table = (unsigned long)efi.systab->tables; if (efi_reuse_config(efi_config_table, efi_nr_tables))
efi.fw_vendor = (unsigned long)efi.systab->fw_vendor;
efi.runtime = (unsigned long)efi.systab->runtime;
/*
* Show what we know for posterity
*/
c16 = early_memremap_ro(efi.systab->fw_vendor,
sizeof(vendor) * sizeof(efi_char16_t));
if (c16) {
for (i = 0; i < sizeof(vendor) - 1 && c16[i]; ++i)
vendor[i] = c16[i];
vendor[i] = '\0';
early_memunmap(c16, sizeof(vendor) * sizeof(efi_char16_t));
} else {
pr_err("Could not map the firmware vendor!\n");
}
pr_info("EFI v%u.%.02u by %s\n",
efi.systab->hdr.revision >> 16,
efi.systab->hdr.revision & 0xffff, vendor);
if (efi_reuse_config(efi.systab->tables, efi.systab->nr_tables))
return; return;
if (efi_config_init(arch_tables)) if (efi_config_init(arch_tables))
@ -497,6 +473,22 @@ void __init efi_init(void)
return; return;
} }
/* Parse the EFI Properties table if it exists */
if (prop_phys != EFI_INVALID_TABLE_ADDR) {
efi_properties_table_t *tbl;
tbl = early_memremap_ro(prop_phys, sizeof(*tbl));
if (tbl == NULL) {
pr_err("Could not map Properties table!\n");
} else {
if (tbl->memory_protection_attribute &
EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA)
set_bit(EFI_NX_PE_DATA, &efi.flags);
early_memunmap(tbl, sizeof(*tbl));
}
}
set_bit(EFI_RUNTIME_SERVICES, &efi.flags); set_bit(EFI_RUNTIME_SERVICES, &efi.flags);
efi_clean_memmap(); efi_clean_memmap();
@ -603,20 +595,6 @@ static void __init efi_merge_regions(void)
} }
} }
static void __init get_systab_virt_addr(efi_memory_desc_t *md)
{
unsigned long size;
u64 end, systab;
size = md->num_pages << EFI_PAGE_SHIFT;
end = md->phys_addr + size;
systab = efi_systab_phys;
if (md->phys_addr <= systab && systab < end) {
systab += md->virt_addr - md->phys_addr;
efi.systab = (efi_system_table_t *)(unsigned long)systab;
}
}
static void *realloc_pages(void *old_memmap, int old_shift) static void *realloc_pages(void *old_memmap, int old_shift)
{ {
void *ret; void *ret;
@ -772,7 +750,6 @@ static void * __init efi_map_regions(int *count, int *pg_shift)
continue; continue;
efi_map_region(md); efi_map_region(md);
get_systab_virt_addr(md);
if (left < desc_size) { if (left < desc_size) {
new_memmap = realloc_pages(new_memmap, *pg_shift); new_memmap = realloc_pages(new_memmap, *pg_shift);
@ -798,8 +775,6 @@ static void __init kexec_enter_virtual_mode(void)
efi_memory_desc_t *md; efi_memory_desc_t *md;
unsigned int num_pages; unsigned int num_pages;
efi.systab = NULL;
/* /*
* We don't do virtual mode, since we don't do runtime services, on * We don't do virtual mode, since we don't do runtime services, on
* non-native EFI. With the UV1 memmap, we don't do runtime services in * non-native EFI. With the UV1 memmap, we don't do runtime services in
@ -822,10 +797,8 @@ 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_each_efi_memory_desc(md) { for_each_efi_memory_desc(md)
efi_map_region_fixed(md); /* FIXME: add error handling */ efi_map_region_fixed(md); /* FIXME: add error handling */
get_systab_virt_addr(md);
}
/* /*
* Unregister the early EFI memmap from efi_init() and install * Unregister the early EFI memmap from efi_init() and install
@ -840,8 +813,6 @@ static void __init kexec_enter_virtual_mode(void)
return; return;
} }
BUG_ON(!efi.systab);
num_pages = ALIGN(efi.memmap.nr_map * efi.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;
@ -851,15 +822,6 @@ static void __init kexec_enter_virtual_mode(void)
} }
efi_sync_low_kernel_mappings(); efi_sync_low_kernel_mappings();
/*
* Now that EFI is in virtual mode, update the function
* pointers in the runtime service table to the new virtual addresses.
*
* Call EFI services through wrapper functions.
*/
efi.runtime_version = efi_systab.hdr.revision;
efi_native_runtime_setup(); efi_native_runtime_setup();
#endif #endif
} }
@ -893,8 +855,6 @@ static void __init __efi_enter_virtual_mode(void)
efi_status_t status; efi_status_t status;
unsigned long pa; unsigned long pa;
efi.systab = NULL;
if (efi_alloc_page_tables()) { if (efi_alloc_page_tables()) {
pr_err("Failed to allocate EFI page tables\n"); pr_err("Failed to allocate EFI page tables\n");
goto err; goto err;
@ -926,9 +886,6 @@ static void __init __efi_enter_virtual_mode(void)
efi_print_memmap(); efi_print_memmap();
} }
if (WARN_ON(!efi.systab))
goto err;
if (efi_setup_page_tables(pa, 1 << pg_shift)) if (efi_setup_page_tables(pa, 1 << pg_shift))
goto err; goto err;
@ -937,7 +894,8 @@ static void __init __efi_enter_virtual_mode(void)
status = efi_set_virtual_address_map(efi.memmap.desc_size * count, status = efi_set_virtual_address_map(efi.memmap.desc_size * count,
efi.memmap.desc_size, efi.memmap.desc_size,
efi.memmap.desc_version, efi.memmap.desc_version,
(efi_memory_desc_t *)pa); (efi_memory_desc_t *)pa,
efi_systab_phys);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
pr_err("Unable to switch EFI into virtual mode (status=%lx)!\n", pr_err("Unable to switch EFI into virtual mode (status=%lx)!\n",
status); status);
@ -947,14 +905,6 @@ static void __init __efi_enter_virtual_mode(void)
efi_check_for_embedded_firmwares(); efi_check_for_embedded_firmwares();
efi_free_boot_services(); efi_free_boot_services();
/*
* Now that EFI is in virtual mode, update the function
* pointers in the runtime service table to the new virtual addresses.
*
* Call EFI services through wrapper functions.
*/
efi.runtime_version = efi_systab.hdr.revision;
if (!efi_is_mixed()) if (!efi_is_mixed())
efi_native_runtime_setup(); efi_native_runtime_setup();
else else
@ -980,6 +930,8 @@ void __init efi_enter_virtual_mode(void)
if (efi_enabled(EFI_PARAVIRT)) if (efi_enabled(EFI_PARAVIRT))
return; return;
efi.runtime = (efi_runtime_services_t *)efi_runtime;
if (efi_setup) if (efi_setup)
kexec_enter_virtual_mode(); kexec_enter_virtual_mode();
else else
@ -1001,3 +953,43 @@ bool efi_is_table_address(unsigned long phys_addr)
return false; return false;
} }
char *efi_systab_show_arch(char *str)
{
if (uga_phys != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "UGA=0x%lx\n", uga_phys);
return str;
}
#define EFI_FIELD(var) efi_ ## var
#define EFI_ATTR_SHOW(name) \
static ssize_t name##_show(struct kobject *kobj, \
struct kobj_attribute *attr, char *buf) \
{ \
return sprintf(buf, "0x%lx\n", EFI_FIELD(name)); \
}
EFI_ATTR_SHOW(fw_vendor);
EFI_ATTR_SHOW(runtime);
EFI_ATTR_SHOW(config_table);
struct kobj_attribute efi_attr_fw_vendor = __ATTR_RO(fw_vendor);
struct kobj_attribute efi_attr_runtime = __ATTR_RO(runtime);
struct kobj_attribute efi_attr_config_table = __ATTR_RO(config_table);
umode_t efi_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n)
{
if (attr == &efi_attr_fw_vendor.attr) {
if (efi_enabled(EFI_PARAVIRT) ||
efi_fw_vendor == EFI_INVALID_TABLE_ADDR)
return 0;
} else if (attr == &efi_attr_runtime.attr) {
if (efi_runtime == EFI_INVALID_TABLE_ADDR)
return 0;
} else if (attr == &efi_attr_config_table.attr) {
if (efi_config_table == EFI_INVALID_TABLE_ADDR)
return 0;
}
return attr->mode;
}

View File

@ -66,14 +66,16 @@ void __init efi_map_region(efi_memory_desc_t *md)
void __init efi_map_region_fixed(efi_memory_desc_t *md) {} void __init efi_map_region_fixed(efi_memory_desc_t *md) {}
void __init parse_efi_setup(u64 phys_addr, u32 data_len) {} void __init parse_efi_setup(u64 phys_addr, u32 data_len) {}
efi_status_t efi_call_svam(efi_set_virtual_address_map_t *__efiapi *, efi_status_t efi_call_svam(efi_runtime_services_t * const *,
u32, u32, u32, void *); u32, u32, u32, void *, u32);
efi_status_t __init efi_set_virtual_address_map(unsigned long memory_map_size, efi_status_t __init efi_set_virtual_address_map(unsigned long memory_map_size,
unsigned long descriptor_size, unsigned long descriptor_size,
u32 descriptor_version, u32 descriptor_version,
efi_memory_desc_t *virtual_map) efi_memory_desc_t *virtual_map,
unsigned long systab_phys)
{ {
const efi_system_table_t *systab = (efi_system_table_t *)systab_phys;
struct desc_ptr gdt_descr; struct desc_ptr gdt_descr;
efi_status_t status; efi_status_t status;
unsigned long flags; unsigned long flags;
@ -90,9 +92,10 @@ efi_status_t __init efi_set_virtual_address_map(unsigned long memory_map_size,
/* Disable interrupts around EFI calls: */ /* Disable interrupts around EFI calls: */
local_irq_save(flags); local_irq_save(flags);
status = efi_call_svam(&efi.systab->runtime->set_virtual_address_map, status = efi_call_svam(&systab->runtime,
memory_map_size, descriptor_size, memory_map_size, descriptor_size,
descriptor_version, virtual_map); descriptor_version, virtual_map,
__pa(&efi.runtime));
local_irq_restore(flags); local_irq_restore(flags);
load_fixmap_gdt(0); load_fixmap_gdt(0);

View File

@ -497,12 +497,9 @@ static DEFINE_SPINLOCK(efi_runtime_lock);
*/ */
#define __efi_thunk(func, ...) \ #define __efi_thunk(func, ...) \
({ \ ({ \
efi_runtime_services_32_t *__rt; \
unsigned short __ds, __es; \ unsigned short __ds, __es; \
efi_status_t ____s; \ efi_status_t ____s; \
\ \
__rt = (void *)(unsigned long)efi.systab->mixed_mode.runtime; \
\
savesegment(ds, __ds); \ savesegment(ds, __ds); \
savesegment(es, __es); \ savesegment(es, __es); \
\ \
@ -510,7 +507,7 @@ static DEFINE_SPINLOCK(efi_runtime_lock);
loadsegment(ds, __KERNEL_DS); \ loadsegment(ds, __KERNEL_DS); \
loadsegment(es, __KERNEL_DS); \ loadsegment(es, __KERNEL_DS); \
\ \
____s = efi64_thunk(__rt->func, __VA_ARGS__); \ ____s = efi64_thunk(efi.runtime->mixed_mode.func, __VA_ARGS__); \
\ \
loadsegment(ds, __ds); \ loadsegment(ds, __ds); \
loadsegment(es, __es); \ loadsegment(es, __es); \
@ -839,8 +836,10 @@ efi_status_t __init __no_sanitize_address
efi_set_virtual_address_map(unsigned long memory_map_size, efi_set_virtual_address_map(unsigned long memory_map_size,
unsigned long descriptor_size, unsigned long descriptor_size,
u32 descriptor_version, u32 descriptor_version,
efi_memory_desc_t *virtual_map) efi_memory_desc_t *virtual_map,
unsigned long systab_phys)
{ {
const efi_system_table_t *systab = (efi_system_table_t *)systab_phys;
efi_status_t status; efi_status_t status;
unsigned long flags; unsigned long flags;
pgd_t *save_pgd = NULL; pgd_t *save_pgd = NULL;
@ -863,13 +862,16 @@ efi_set_virtual_address_map(unsigned long memory_map_size,
/* Disable interrupts around EFI calls: */ /* Disable interrupts around EFI calls: */
local_irq_save(flags); local_irq_save(flags);
status = efi_call(efi.systab->runtime->set_virtual_address_map, status = efi_call(efi.runtime->set_virtual_address_map,
memory_map_size, descriptor_size, memory_map_size, descriptor_size,
descriptor_version, virtual_map); descriptor_version, virtual_map);
local_irq_restore(flags); local_irq_restore(flags);
kernel_fpu_end(); kernel_fpu_end();
/* grab the virtually remapped EFI runtime services table pointer */
efi.runtime = READ_ONCE(systab->runtime);
if (save_pgd) if (save_pgd)
efi_uv1_memmap_phys_epilog(save_pgd); efi_uv1_memmap_phys_epilog(save_pgd);
else else

View File

@ -8,14 +8,20 @@
#include <linux/linkage.h> #include <linux/linkage.h>
#include <linux/init.h> #include <linux/init.h>
#include <asm/asm-offsets.h>
#include <asm/page_types.h> #include <asm/page_types.h>
__INIT __INIT
SYM_FUNC_START(efi_call_svam) SYM_FUNC_START(efi_call_svam)
push 8(%esp) push %ebp
push 8(%esp) movl %esp, %ebp
push %ebx
push 16(%esp)
push 16(%esp)
push %ecx push %ecx
push %edx push %edx
movl %eax, %ebx // &systab_phys->runtime
/* /*
* Switch to the flat mapped alias of this routine, by jumping to the * Switch to the flat mapped alias of this routine, by jumping to the
@ -35,15 +41,20 @@ SYM_FUNC_START(efi_call_svam)
subl $__PAGE_OFFSET, %esp subl $__PAGE_OFFSET, %esp
/* call the EFI routine */ /* call the EFI routine */
call *(%eax) movl (%eax), %eax
call *EFI_svam(%eax)
/* convert ESP back to a kernel VA, and pop the outgoing args */ /* grab the virtually remapped EFI runtime services table pointer */
addl $__PAGE_OFFSET + 16, %esp movl (%ebx), %ecx
movl 36(%esp), %edx // &efi.runtime
movl %ecx, (%edx)
/* re-enable paging */ /* re-enable paging */
movl %cr0, %edx movl %cr0, %edx
orl $0x80000000, %edx orl $0x80000000, %edx
movl %edx, %cr0 movl %edx, %cr0
movl 16(%esp), %ebx
leave
ret ret
SYM_FUNC_END(efi_call_svam) SYM_FUNC_END(efi_call_svam)

View File

@ -541,7 +541,7 @@ int __init efi_reuse_config(u64 tables, int nr_tables)
goto out_memremap; goto out_memremap;
} }
for (i = 0; i < efi.systab->nr_tables; i++) { for (i = 0; i < nr_tables; i++) {
efi_guid_t guid; efi_guid_t guid;
guid = ((efi_config_table_64_t *)p)->guid; guid = ((efi_config_table_64_t *)p)->guid;

View File

@ -656,6 +656,31 @@ static int find_valid_gpt(struct parsed_partitions *state, gpt_header **gpt,
return 0; return 0;
} }
/**
* utf16_le_to_7bit(): Naively converts a UTF-16LE string to 7-bit ASCII characters
* @in: input UTF-16LE string
* @size: size of the input string
* @out: output string ptr, should be capable to store @size+1 characters
*
* Description: Converts @size UTF16-LE symbols from @in string to 7-bit
* ASCII characters and stores them to @out. Adds trailing zero to @out array.
*/
static void utf16_le_to_7bit(const __le16 *in, unsigned int size, u8 *out)
{
unsigned int i = 0;
out[size] = 0;
while (i < size) {
u8 c = le16_to_cpu(in[i]) & 0xff;
if (c && !isprint(c))
c = '!';
out[i] = c;
i++;
}
}
/** /**
* efi_partition(struct parsed_partitions *state) * efi_partition(struct parsed_partitions *state)
* @state: disk parsed partitions * @state: disk parsed partitions
@ -692,7 +717,6 @@ int efi_partition(struct parsed_partitions *state)
for (i = 0; i < le32_to_cpu(gpt->num_partition_entries) && i < state->limit-1; i++) { for (i = 0; i < le32_to_cpu(gpt->num_partition_entries) && i < state->limit-1; i++) {
struct partition_meta_info *info; struct partition_meta_info *info;
unsigned label_count = 0;
unsigned label_max; unsigned label_max;
u64 start = le64_to_cpu(ptes[i].starting_lba); u64 start = le64_to_cpu(ptes[i].starting_lba);
u64 size = le64_to_cpu(ptes[i].ending_lba) - u64 size = le64_to_cpu(ptes[i].ending_lba) -
@ -713,14 +737,7 @@ int efi_partition(struct parsed_partitions *state)
/* Naively convert UTF16-LE to 7 bits. */ /* Naively convert UTF16-LE to 7 bits. */
label_max = min(ARRAY_SIZE(info->volname) - 1, label_max = min(ARRAY_SIZE(info->volname) - 1,
ARRAY_SIZE(ptes[i].partition_name)); ARRAY_SIZE(ptes[i].partition_name));
info->volname[label_max] = 0; utf16_le_to_7bit(ptes[i].partition_name, label_max, info->volname);
while (label_count < label_max) {
u8 c = ptes[i].partition_name[label_count] & 0xff;
if (c && !isprint(c))
c = '!';
info->volname[label_count] = c;
label_count++;
}
state->parts[i + 1].has_info = true; state->parts[i + 1].has_info = true;
} }
kfree(ptes); kfree(ptes);

View File

@ -88,7 +88,7 @@ typedef struct _gpt_entry {
__le64 starting_lba; __le64 starting_lba;
__le64 ending_lba; __le64 ending_lba;
gpt_entry_attributes attributes; gpt_entry_attributes attributes;
efi_char16_t partition_name[72 / sizeof (efi_char16_t)]; __le16 partition_name[72/sizeof(__le16)];
} __packed gpt_entry; } __packed gpt_entry;
typedef struct _gpt_mbr_record { typedef struct _gpt_mbr_record {

View File

@ -13,13 +13,14 @@ KASAN_SANITIZE_runtime-wrappers.o := n
obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o
obj-$(CONFIG_EFI) += efi.o vars.o reboot.o memattr.o tpm.o obj-$(CONFIG_EFI) += efi.o vars.o reboot.o memattr.o tpm.o
obj-$(CONFIG_EFI) += capsule.o memmap.o obj-$(CONFIG_EFI) += capsule.o memmap.o
obj-$(CONFIG_EFI_PARAMS_FROM_FDT) += fdtparams.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
obj-$(CONFIG_UEFI_CPER) += cper.o obj-$(CONFIG_UEFI_CPER) += cper.o
obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o 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/ subdir-$(CONFIG_EFI_STUB) += libstub
obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_map.o obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_map.o
obj-$(CONFIG_EFI_BOOTLOADER_CONTROL) += efibc.o obj-$(CONFIG_EFI_BOOTLOADER_CONTROL) += efibc.o
obj-$(CONFIG_EFI_TEST) += test/ obj-$(CONFIG_EFI_TEST) += test/

View File

@ -31,7 +31,7 @@ __setup("dump_apple_properties", dump_properties_enable);
struct dev_header { struct dev_header {
u32 len; u32 len;
u32 prop_count; u32 prop_count;
struct efi_dev_path path[0]; struct efi_dev_path path[];
/* /*
* followed by key/value pairs, each key and value preceded by u32 len, * followed by key/value pairs, each key and value preceded by u32 len,
* len includes itself, value may be empty (in which case its len is 4) * len includes itself, value may be empty (in which case its len is 4)
@ -42,11 +42,11 @@ struct properties_header {
u32 len; u32 len;
u32 version; u32 version;
u32 dev_count; u32 dev_count;
struct dev_header dev_header[0]; struct dev_header dev_header[];
}; };
static void __init unmarshal_key_value_pairs(struct dev_header *dev_header, static void __init unmarshal_key_value_pairs(struct dev_header *dev_header,
struct device *dev, void *ptr, struct device *dev, const void *ptr,
struct property_entry entry[]) struct property_entry entry[])
{ {
int i; int i;
@ -117,10 +117,10 @@ static int __init unmarshal_devices(struct properties_header *properties)
while (offset + sizeof(struct dev_header) < properties->len) { while (offset + sizeof(struct dev_header) < properties->len) {
struct dev_header *dev_header = (void *)properties + offset; struct dev_header *dev_header = (void *)properties + offset;
struct property_entry *entry = NULL; struct property_entry *entry = NULL;
const struct efi_dev_path *ptr;
struct device *dev; struct device *dev;
size_t len; size_t len;
int ret, i; int ret, i;
void *ptr;
if (offset + dev_header->len > properties->len || if (offset + dev_header->len > properties->len ||
dev_header->len <= sizeof(*dev_header)) { dev_header->len <= sizeof(*dev_header)) {
@ -131,10 +131,10 @@ static int __init unmarshal_devices(struct properties_header *properties)
ptr = dev_header->path; ptr = dev_header->path;
len = dev_header->len - sizeof(*dev_header); len = dev_header->len - sizeof(*dev_header);
dev = efi_get_device_by_path((struct efi_dev_path **)&ptr, &len); dev = efi_get_device_by_path(&ptr, &len);
if (IS_ERR(dev)) { if (IS_ERR(dev)) {
pr_err("device path parse error %ld at %#zx:\n", pr_err("device path parse error %ld at %#zx:\n",
PTR_ERR(dev), ptr - (void *)dev_header); PTR_ERR(dev), (void *)ptr - (void *)dev_header);
print_hex_dump(KERN_ERR, pr_fmt(), DUMP_PREFIX_OFFSET, print_hex_dump(KERN_ERR, pr_fmt(), DUMP_PREFIX_OFFSET,
16, 1, dev_header, dev_header->len, true); 16, 1, dev_header, dev_header->len, true);
dev = NULL; dev = NULL;

View File

@ -22,8 +22,6 @@
#include <asm/efi.h> #include <asm/efi.h>
u64 efi_system_table;
static int __init is_memory(efi_memory_desc_t *md) static int __init is_memory(efi_memory_desc_t *md)
{ {
if (md->attribute & (EFI_MEMORY_WB|EFI_MEMORY_WT|EFI_MEMORY_WC)) if (md->attribute & (EFI_MEMORY_WB|EFI_MEMORY_WT|EFI_MEMORY_WC))
@ -36,7 +34,7 @@ static int __init is_memory(efi_memory_desc_t *md)
* as some data members of the EFI system table are virtually remapped after * as some data members of the EFI system table are virtually remapped after
* SetVirtualAddressMap() has been called. * SetVirtualAddressMap() has been called.
*/ */
static phys_addr_t efi_to_phys(unsigned long addr) static phys_addr_t __init efi_to_phys(unsigned long addr)
{ {
efi_memory_desc_t *md; efi_memory_desc_t *md;
@ -55,7 +53,7 @@ static phys_addr_t efi_to_phys(unsigned long addr)
static __initdata unsigned long screen_info_table = EFI_INVALID_TABLE_ADDR; static __initdata unsigned long screen_info_table = EFI_INVALID_TABLE_ADDR;
static __initdata efi_config_table_type_t arch_tables[] = { static const efi_config_table_type_t arch_tables[] __initconst = {
{LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, NULL, &screen_info_table}, {LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, NULL, &screen_info_table},
{NULL_GUID, NULL, NULL} {NULL_GUID, NULL, NULL}
}; };
@ -83,17 +81,15 @@ static void __init init_screen_info(void)
memblock_mark_nomap(screen_info.lfb_base, screen_info.lfb_size); memblock_mark_nomap(screen_info.lfb_base, screen_info.lfb_size);
} }
static int __init uefi_init(void) static int __init uefi_init(u64 efi_system_table)
{ {
efi_char16_t *c16; efi_config_table_t *config_tables;
void *config_tables; efi_system_table_t *systab;
size_t table_size; size_t table_size;
char vendor[100] = "unknown"; int retval;
int i, retval;
efi.systab = early_memremap_ro(efi_system_table, systab = early_memremap_ro(efi_system_table, sizeof(efi_system_table_t));
sizeof(efi_system_table_t)); if (systab == NULL) {
if (efi.systab == NULL) {
pr_warn("Unable to map EFI system table.\n"); pr_warn("Unable to map EFI system table.\n");
return -ENOMEM; return -ENOMEM;
} }
@ -102,53 +98,29 @@ static int __init uefi_init(void)
if (IS_ENABLED(CONFIG_64BIT)) if (IS_ENABLED(CONFIG_64BIT))
set_bit(EFI_64BIT, &efi.flags); set_bit(EFI_64BIT, &efi.flags);
/* retval = efi_systab_check_header(&systab->hdr, 2);
* Verify the EFI Table if (retval)
*/
if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) {
pr_err("System table signature incorrect\n");
retval = -EINVAL;
goto out; goto out;
}
if ((efi.systab->hdr.revision >> 16) < 2)
pr_warn("Warning: EFI system table version %d.%02d, expected 2.00 or greater\n",
efi.systab->hdr.revision >> 16,
efi.systab->hdr.revision & 0xffff);
efi.runtime_version = efi.systab->hdr.revision; efi.runtime = systab->runtime;
efi.runtime_version = systab->hdr.revision;
/* Show what we know for posterity */ efi_systab_report_header(&systab->hdr, efi_to_phys(systab->fw_vendor));
c16 = early_memremap_ro(efi_to_phys(efi.systab->fw_vendor),
sizeof(vendor) * sizeof(efi_char16_t));
if (c16) {
for (i = 0; i < (int) sizeof(vendor) - 1 && *c16; ++i)
vendor[i] = c16[i];
vendor[i] = '\0';
early_memunmap(c16, sizeof(vendor) * sizeof(efi_char16_t));
}
pr_info("EFI v%u.%.02u by %s\n", table_size = sizeof(efi_config_table_t) * systab->nr_tables;
efi.systab->hdr.revision >> 16, config_tables = early_memremap_ro(efi_to_phys(systab->tables),
efi.systab->hdr.revision & 0xffff, vendor);
table_size = sizeof(efi_config_table_64_t) * efi.systab->nr_tables;
config_tables = early_memremap_ro(efi_to_phys(efi.systab->tables),
table_size); table_size);
if (config_tables == NULL) { if (config_tables == NULL) {
pr_warn("Unable to map EFI config table array.\n"); pr_warn("Unable to map EFI config table array.\n");
retval = -ENOMEM; retval = -ENOMEM;
goto out; goto out;
} }
retval = efi_config_parse_tables(config_tables, efi.systab->nr_tables, retval = efi_config_parse_tables(config_tables, systab->nr_tables,
sizeof(efi_config_table_t),
arch_tables); arch_tables);
if (!retval)
efi.config_table = (unsigned long)efi.systab->tables;
early_memunmap(config_tables, table_size); early_memunmap(config_tables, table_size);
out: out:
early_memunmap(efi.systab, sizeof(efi_system_table_t)); early_memunmap(systab, sizeof(efi_system_table_t));
return retval; return retval;
} }
@ -233,19 +205,13 @@ static __init void reserve_regions(void)
void __init efi_init(void) void __init efi_init(void)
{ {
struct efi_memory_map_data data; struct efi_memory_map_data data;
struct efi_fdt_params params; u64 efi_system_table;
/* Grab UEFI information placed in FDT by stub */ /* Grab UEFI information placed in FDT by stub */
if (!efi_get_fdt_params(&params)) efi_system_table = efi_get_fdt_params(&data);
if (!efi_system_table)
return; return;
efi_system_table = params.system_table;
data.desc_version = params.desc_ver;
data.desc_size = params.desc_size;
data.size = params.mmap_size;
data.phys_map = params.mmap;
if (efi_memmap_init_early(&data) < 0) { if (efi_memmap_init_early(&data) < 0) {
/* /*
* 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
@ -259,7 +225,7 @@ void __init efi_init(void)
"Unexpected EFI_MEMORY_DESCRIPTOR version %ld", "Unexpected EFI_MEMORY_DESCRIPTOR version %ld",
efi.memmap.desc_version); efi.memmap.desc_version);
if (uefi_init() < 0) { if (uefi_init(efi_system_table) < 0) {
efi_memmap_unmap(); efi_memmap_unmap();
return; return;
} }
@ -267,9 +233,8 @@ void __init efi_init(void)
reserve_regions(); reserve_regions();
efi_esrt_init(); efi_esrt_init();
memblock_reserve(params.mmap & PAGE_MASK, memblock_reserve(data.phys_map & PAGE_MASK,
PAGE_ALIGN(params.mmap_size + PAGE_ALIGN(data.size + (data.phys_map & ~PAGE_MASK)));
(params.mmap & ~PAGE_MASK)));
init_screen_info(); init_screen_info();

View File

@ -25,8 +25,6 @@
#include <asm/pgalloc.h> #include <asm/pgalloc.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
extern u64 efi_system_table;
#if defined(CONFIG_PTDUMP_DEBUGFS) && defined(CONFIG_ARM64) #if defined(CONFIG_PTDUMP_DEBUGFS) && defined(CONFIG_ARM64)
#include <asm/ptdump.h> #include <asm/ptdump.h>
@ -54,13 +52,11 @@ device_initcall(ptdump_init);
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);
mm_init_cpumask(&efi_mm); mm_init_cpumask(&efi_mm);
init_new_context(NULL, &efi_mm); init_new_context(NULL, &efi_mm);
systab_found = false;
for_each_efi_memory_desc(md) { for_each_efi_memory_desc(md) {
phys_addr_t phys = md->phys_addr; phys_addr_t phys = md->phys_addr;
int ret; int ret;
@ -76,20 +72,6 @@ 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)) if (efi_memattr_apply_permissions(&efi_mm, efi_set_mapping_permissions))

View File

@ -168,7 +168,7 @@ static ssize_t efi_capsule_submit_update(struct capsule_info *cap_info)
static ssize_t efi_capsule_write(struct file *file, const char __user *buff, static ssize_t efi_capsule_write(struct file *file, const char __user *buff,
size_t count, loff_t *offp) size_t count, loff_t *offp)
{ {
int ret = 0; int ret;
struct capsule_info *cap_info = file->private_data; struct capsule_info *cap_info = file->private_data;
struct page *page; struct page *page;
void *kbuff = NULL; void *kbuff = NULL;

View File

@ -31,13 +31,13 @@ static int __init match_acpi_dev(struct device *dev, const void *data)
return !strcmp("0", hid_uid.uid); return !strcmp("0", hid_uid.uid);
} }
static long __init parse_acpi_path(struct efi_dev_path *node, static long __init parse_acpi_path(const struct efi_dev_path *node,
struct device *parent, struct device **child) struct device *parent, struct device **child)
{ {
struct acpi_hid_uid hid_uid = {}; struct acpi_hid_uid hid_uid = {};
struct device *phys_dev; struct device *phys_dev;
if (node->length != 12) if (node->header.length != 12)
return -EINVAL; return -EINVAL;
sprintf(hid_uid.hid[0].id, "%c%c%c%04X", sprintf(hid_uid.hid[0].id, "%c%c%c%04X",
@ -69,12 +69,12 @@ static int __init match_pci_dev(struct device *dev, void *data)
return dev_is_pci(dev) && to_pci_dev(dev)->devfn == devfn; return dev_is_pci(dev) && to_pci_dev(dev)->devfn == devfn;
} }
static long __init parse_pci_path(struct efi_dev_path *node, static long __init parse_pci_path(const struct efi_dev_path *node,
struct device *parent, struct device **child) struct device *parent, struct device **child)
{ {
unsigned int devfn; unsigned int devfn;
if (node->length != 6) if (node->header.length != 6)
return -EINVAL; return -EINVAL;
if (!parent) if (!parent)
return -EINVAL; return -EINVAL;
@ -105,19 +105,19 @@ static long __init parse_pci_path(struct efi_dev_path *node,
* search for a device. * search for a device.
*/ */
static long __init parse_end_path(struct efi_dev_path *node, static long __init parse_end_path(const struct efi_dev_path *node,
struct device *parent, struct device **child) struct device *parent, struct device **child)
{ {
if (node->length != 4) if (node->header.length != 4)
return -EINVAL; return -EINVAL;
if (node->sub_type != EFI_DEV_END_INSTANCE && if (node->header.sub_type != EFI_DEV_END_INSTANCE &&
node->sub_type != EFI_DEV_END_ENTIRE) node->header.sub_type != EFI_DEV_END_ENTIRE)
return -EINVAL; return -EINVAL;
if (!parent) if (!parent)
return -ENODEV; return -ENODEV;
*child = get_device(parent); *child = get_device(parent);
return node->sub_type; return node->header.sub_type;
} }
/** /**
@ -156,7 +156,7 @@ static long __init parse_end_path(struct efi_dev_path *node,
* %ERR_PTR(-EINVAL) if a node is malformed or exceeds @len, * %ERR_PTR(-EINVAL) if a node is malformed or exceeds @len,
* %ERR_PTR(-ENOTSUPP) if support for a node type is not yet implemented. * %ERR_PTR(-ENOTSUPP) if support for a node type is not yet implemented.
*/ */
struct device * __init efi_get_device_by_path(struct efi_dev_path **node, struct device * __init efi_get_device_by_path(const struct efi_dev_path **node,
size_t *len) size_t *len)
{ {
struct device *parent = NULL, *child; struct device *parent = NULL, *child;
@ -166,16 +166,16 @@ struct device * __init efi_get_device_by_path(struct efi_dev_path **node,
return NULL; return NULL;
while (!ret) { while (!ret) {
if (*len < 4 || *len < (*node)->length) if (*len < 4 || *len < (*node)->header.length)
ret = -EINVAL; ret = -EINVAL;
else if ((*node)->type == EFI_DEV_ACPI && else if ((*node)->header.type == EFI_DEV_ACPI &&
(*node)->sub_type == EFI_DEV_BASIC_ACPI) (*node)->header.sub_type == EFI_DEV_BASIC_ACPI)
ret = parse_acpi_path(*node, parent, &child); ret = parse_acpi_path(*node, parent, &child);
else if ((*node)->type == EFI_DEV_HW && else if ((*node)->header.type == EFI_DEV_HW &&
(*node)->sub_type == EFI_DEV_PCI) (*node)->header.sub_type == EFI_DEV_PCI)
ret = parse_pci_path(*node, parent, &child); ret = parse_pci_path(*node, parent, &child);
else if (((*node)->type == EFI_DEV_END_PATH || else if (((*node)->header.type == EFI_DEV_END_PATH ||
(*node)->type == EFI_DEV_END_PATH2)) (*node)->header.type == EFI_DEV_END_PATH2))
ret = parse_end_path(*node, parent, &child); ret = parse_end_path(*node, parent, &child);
else else
ret = -ENOTSUPP; ret = -ENOTSUPP;
@ -185,8 +185,8 @@ struct device * __init efi_get_device_by_path(struct efi_dev_path **node,
return ERR_PTR(ret); return ERR_PTR(ret);
parent = child; parent = child;
*node = (void *)*node + (*node)->length; *node = (void *)*node + (*node)->header.length;
*len -= (*node)->length; *len -= (*node)->header.length;
} }
if (ret == EFI_DEV_END_ENTIRE) if (ret == EFI_DEV_END_ENTIRE)

View File

@ -42,7 +42,12 @@ void __init efi_bgrt_init(struct acpi_table_header *table)
return; return;
} }
*bgrt = *(struct acpi_table_bgrt *)table; *bgrt = *(struct acpi_table_bgrt *)table;
if (bgrt->version != 1) { /*
* Only version 1 is defined but some older laptops (seen on Lenovo
* Ivy Bridge models) have a correct version 1 BGRT table with the
* version set to 0, so we accept version 0 and 1.
*/
if (bgrt->version > 1) {
pr_notice("Ignoring BGRT: invalid version %u (expected 1)\n", pr_notice("Ignoring BGRT: invalid version %u (expected 1)\n",
bgrt->version); bgrt->version);
goto out; goto out;

View File

@ -161,7 +161,7 @@ static int efi_pstore_scan_sysfs_exit(struct efivar_entry *pos,
* *
* @record: pstore record to pass to callback * @record: pstore record to pass to callback
* *
* You MUST call efivar_enter_iter_begin() before this function, and * You MUST call efivar_entry_iter_begin() before this function, and
* efivar_entry_iter_end() afterwards. * efivar_entry_iter_end() afterwards.
* *
*/ */
@ -356,7 +356,7 @@ static struct pstore_info efi_pstore_info = {
static __init int efivars_pstore_init(void) static __init int efivars_pstore_init(void)
{ {
if (!efi_enabled(EFI_RUNTIME_SERVICES)) if (!efi_rt_services_supported(EFI_RT_SUPPORTED_VARIABLE_SERVICES))
return 0; return 0;
if (!efivars_kobject()) if (!efivars_kobject())

View File

@ -21,7 +21,6 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/efi.h> #include <linux/efi.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/kexec.h> #include <linux/kexec.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
@ -36,27 +35,21 @@
#include <asm/early_ioremap.h> #include <asm/early_ioremap.h>
struct efi __read_mostly efi = { struct efi __read_mostly efi = {
.mps = EFI_INVALID_TABLE_ADDR, .runtime_supported_mask = EFI_RT_SUPPORTED_ALL,
.acpi = EFI_INVALID_TABLE_ADDR, .acpi = EFI_INVALID_TABLE_ADDR,
.acpi20 = EFI_INVALID_TABLE_ADDR, .acpi20 = EFI_INVALID_TABLE_ADDR,
.smbios = EFI_INVALID_TABLE_ADDR, .smbios = EFI_INVALID_TABLE_ADDR,
.smbios3 = EFI_INVALID_TABLE_ADDR, .smbios3 = EFI_INVALID_TABLE_ADDR,
.boot_info = EFI_INVALID_TABLE_ADDR,
.hcdp = EFI_INVALID_TABLE_ADDR,
.uga = EFI_INVALID_TABLE_ADDR,
.fw_vendor = EFI_INVALID_TABLE_ADDR,
.runtime = 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,
.mem_attr_table = EFI_INVALID_TABLE_ADDR,
.rng_seed = EFI_INVALID_TABLE_ADDR,
.tpm_log = EFI_INVALID_TABLE_ADDR, .tpm_log = EFI_INVALID_TABLE_ADDR,
.tpm_final_log = EFI_INVALID_TABLE_ADDR, .tpm_final_log = EFI_INVALID_TABLE_ADDR,
.mem_reserve = EFI_INVALID_TABLE_ADDR,
}; };
EXPORT_SYMBOL(efi); EXPORT_SYMBOL(efi);
unsigned long __ro_after_init efi_rng_seed = EFI_INVALID_TABLE_ADDR;
static unsigned long __initdata mem_reserve = EFI_INVALID_TABLE_ADDR;
static unsigned long __initdata rt_prop = EFI_INVALID_TABLE_ADDR;
struct mm_struct efi_mm = { struct mm_struct efi_mm = {
.mm_rb = RB_ROOT, .mm_rb = RB_ROOT,
.mm_users = ATOMIC_INIT(2), .mm_users = ATOMIC_INIT(2),
@ -123,8 +116,6 @@ static ssize_t systab_show(struct kobject *kobj,
if (!kobj || !buf) if (!kobj || !buf)
return -EINVAL; return -EINVAL;
if (efi.mps != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "MPS=0x%lx\n", efi.mps);
if (efi.acpi20 != EFI_INVALID_TABLE_ADDR) if (efi.acpi20 != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "ACPI20=0x%lx\n", efi.acpi20); str += sprintf(str, "ACPI20=0x%lx\n", efi.acpi20);
if (efi.acpi != EFI_INVALID_TABLE_ADDR) if (efi.acpi != EFI_INVALID_TABLE_ADDR)
@ -138,67 +129,42 @@ static ssize_t systab_show(struct kobject *kobj,
str += sprintf(str, "SMBIOS3=0x%lx\n", efi.smbios3); str += sprintf(str, "SMBIOS3=0x%lx\n", efi.smbios3);
if (efi.smbios != EFI_INVALID_TABLE_ADDR) if (efi.smbios != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios); str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios);
if (efi.hcdp != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "HCDP=0x%lx\n", efi.hcdp); if (IS_ENABLED(CONFIG_IA64) || IS_ENABLED(CONFIG_X86)) {
if (efi.boot_info != EFI_INVALID_TABLE_ADDR) extern char *efi_systab_show_arch(char *str);
str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info);
if (efi.uga != EFI_INVALID_TABLE_ADDR) str = efi_systab_show_arch(str);
str += sprintf(str, "UGA=0x%lx\n", efi.uga); }
return str - buf; return str - buf;
} }
static struct kobj_attribute efi_attr_systab = __ATTR_RO_MODE(systab, 0400); static struct kobj_attribute efi_attr_systab = __ATTR_RO_MODE(systab, 0400);
#define EFI_FIELD(var) efi.var
#define EFI_ATTR_SHOW(name) \
static ssize_t name##_show(struct kobject *kobj, \
struct kobj_attribute *attr, char *buf) \
{ \
return sprintf(buf, "0x%lx\n", EFI_FIELD(name)); \
}
EFI_ATTR_SHOW(fw_vendor);
EFI_ATTR_SHOW(runtime);
EFI_ATTR_SHOW(config_table);
static ssize_t fw_platform_size_show(struct kobject *kobj, static ssize_t fw_platform_size_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf) struct kobj_attribute *attr, char *buf)
{ {
return sprintf(buf, "%d\n", efi_enabled(EFI_64BIT) ? 64 : 32); return sprintf(buf, "%d\n", efi_enabled(EFI_64BIT) ? 64 : 32);
} }
static struct kobj_attribute efi_attr_fw_vendor = __ATTR_RO(fw_vendor); extern __weak struct kobj_attribute efi_attr_fw_vendor;
static struct kobj_attribute efi_attr_runtime = __ATTR_RO(runtime); extern __weak struct kobj_attribute efi_attr_runtime;
static struct kobj_attribute efi_attr_config_table = __ATTR_RO(config_table); extern __weak struct kobj_attribute efi_attr_config_table;
static struct kobj_attribute efi_attr_fw_platform_size = static struct kobj_attribute efi_attr_fw_platform_size =
__ATTR_RO(fw_platform_size); __ATTR_RO(fw_platform_size);
static struct attribute *efi_subsys_attrs[] = { static struct attribute *efi_subsys_attrs[] = {
&efi_attr_systab.attr, &efi_attr_systab.attr,
&efi_attr_fw_platform_size.attr,
&efi_attr_fw_vendor.attr, &efi_attr_fw_vendor.attr,
&efi_attr_runtime.attr, &efi_attr_runtime.attr,
&efi_attr_config_table.attr, &efi_attr_config_table.attr,
&efi_attr_fw_platform_size.attr,
NULL, NULL,
}; };
static umode_t efi_attr_is_visible(struct kobject *kobj, umode_t __weak efi_attr_is_visible(struct kobject *kobj, struct attribute *attr,
struct attribute *attr, int n) int n)
{ {
if (attr == &efi_attr_fw_vendor.attr) {
if (efi_enabled(EFI_PARAVIRT) ||
efi.fw_vendor == EFI_INVALID_TABLE_ADDR)
return 0;
} else if (attr == &efi_attr_runtime.attr) {
if (efi.runtime == EFI_INVALID_TABLE_ADDR)
return 0;
} else if (attr == &efi_attr_config_table.attr) {
if (efi.config_table == EFI_INVALID_TABLE_ADDR)
return 0;
}
return attr->mode; return attr->mode;
} }
@ -388,21 +354,30 @@ static int __init efisubsys_init(void)
{ {
int error; int error;
if (!efi_enabled(EFI_RUNTIME_SERVICES))
efi.runtime_supported_mask = 0;
if (!efi_enabled(EFI_BOOT)) if (!efi_enabled(EFI_BOOT))
return 0; return 0;
/* if (efi.runtime_supported_mask) {
* Since we process only one efi_runtime_service() at a time, an /*
* ordered workqueue (which creates only one execution context) * Since we process only one efi_runtime_service() at a time, an
* should suffice all our needs. * ordered workqueue (which creates only one execution context)
*/ * should suffice for all our needs.
efi_rts_wq = alloc_ordered_workqueue("efi_rts_wq", 0); */
if (!efi_rts_wq) { efi_rts_wq = alloc_ordered_workqueue("efi_rts_wq", 0);
pr_err("Creating efi_rts_wq failed, EFI runtime services disabled.\n"); if (!efi_rts_wq) {
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); pr_err("Creating efi_rts_wq failed, EFI runtime services disabled.\n");
return 0; clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
efi.runtime_supported_mask = 0;
return 0;
}
} }
if (efi_rt_services_supported(EFI_RT_SUPPORTED_TIME_SERVICES))
platform_device_register_simple("rtc-efi", 0, NULL, 0);
/* We register the efi directory at /sys/firmware/efi */ /* We register the efi directory at /sys/firmware/efi */
efi_kobj = kobject_create_and_add("efi", firmware_kobj); efi_kobj = kobject_create_and_add("efi", firmware_kobj);
if (!efi_kobj) { if (!efi_kobj) {
@ -410,12 +385,13 @@ static int __init efisubsys_init(void)
return -ENOMEM; return -ENOMEM;
} }
error = generic_ops_register(); if (efi_rt_services_supported(EFI_RT_SUPPORTED_VARIABLE_SERVICES)) {
if (error)
goto err_put;
if (efi_enabled(EFI_RUNTIME_SERVICES))
efivar_ssdt_load(); efivar_ssdt_load();
error = generic_ops_register();
if (error)
goto err_put;
platform_device_register_simple("efivars", 0, NULL, 0);
}
error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group); error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group);
if (error) { if (error) {
@ -443,7 +419,8 @@ static int __init efisubsys_init(void)
err_remove_group: err_remove_group:
sysfs_remove_group(efi_kobj, &efi_subsys_attr_group); sysfs_remove_group(efi_kobj, &efi_subsys_attr_group);
err_unregister: err_unregister:
generic_ops_unregister(); if (efi_rt_services_supported(EFI_RT_SUPPORTED_VARIABLE_SERVICES))
generic_ops_unregister();
err_put: err_put:
kobject_put(efi_kobj); kobject_put(efi_kobj);
return error; return error;
@ -524,30 +501,27 @@ void __init efi_mem_reserve(phys_addr_t addr, u64 size)
efi_arch_mem_reserve(addr, size); efi_arch_mem_reserve(addr, size);
} }
static __initdata efi_config_table_type_t common_tables[] = { static const efi_config_table_type_t common_tables[] __initconst = {
{ACPI_20_TABLE_GUID, "ACPI 2.0", &efi.acpi20}, {ACPI_20_TABLE_GUID, "ACPI 2.0", &efi.acpi20},
{ACPI_TABLE_GUID, "ACPI", &efi.acpi}, {ACPI_TABLE_GUID, "ACPI", &efi.acpi},
{HCDP_TABLE_GUID, "HCDP", &efi.hcdp},
{MPS_TABLE_GUID, "MPS", &efi.mps},
{SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios}, {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios},
{SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3}, {SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3},
{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_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi_mem_attr_table},
{EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi.mem_attr_table}, {LINUX_EFI_RANDOM_SEED_TABLE_GUID, "RNG", &efi_rng_seed},
{LINUX_EFI_RANDOM_SEED_TABLE_GUID, "RNG", &efi.rng_seed},
{LINUX_EFI_TPM_EVENT_LOG_GUID, "TPMEventLog", &efi.tpm_log}, {LINUX_EFI_TPM_EVENT_LOG_GUID, "TPMEventLog", &efi.tpm_log},
{LINUX_EFI_TPM_FINAL_LOG_GUID, "TPMFinalLog", &efi.tpm_final_log}, {LINUX_EFI_TPM_FINAL_LOG_GUID, "TPMFinalLog", &efi.tpm_final_log},
{LINUX_EFI_MEMRESERVE_TABLE_GUID, "MEMRESERVE", &efi.mem_reserve}, {LINUX_EFI_MEMRESERVE_TABLE_GUID, "MEMRESERVE", &mem_reserve},
{EFI_RT_PROPERTIES_TABLE_GUID, "RTPROP", &rt_prop},
#ifdef CONFIG_EFI_RCI2_TABLE #ifdef CONFIG_EFI_RCI2_TABLE
{DELLEMC_EFI_RCI2_TABLE_GUID, NULL, &rci2_table_phys}, {DELLEMC_EFI_RCI2_TABLE_GUID, NULL, &rci2_table_phys},
#endif #endif
{NULL_GUID, NULL, NULL}, {NULL_GUID, NULL, NULL},
}; };
static __init int match_config_table(efi_guid_t *guid, static __init int match_config_table(const efi_guid_t *guid,
unsigned long table, unsigned long table,
efi_config_table_type_t *table_types) const efi_config_table_type_t *table_types)
{ {
int i; int i;
@ -566,48 +540,47 @@ static __init int match_config_table(efi_guid_t *guid,
return 0; return 0;
} }
int __init efi_config_parse_tables(void *config_tables, int count, int sz, int __init efi_config_parse_tables(const efi_config_table_t *config_tables,
efi_config_table_type_t *arch_tables) int count,
const efi_config_table_type_t *arch_tables)
{ {
void *tablep; const efi_config_table_64_t *tbl64 = (void *)config_tables;
const efi_config_table_32_t *tbl32 = (void *)config_tables;
const efi_guid_t *guid;
unsigned long table;
int i; int i;
tablep = config_tables;
pr_info(""); pr_info("");
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
efi_guid_t guid; if (!IS_ENABLED(CONFIG_X86)) {
unsigned long table; guid = &config_tables[i].guid;
table = (unsigned long)config_tables[i].table;
} else if (efi_enabled(EFI_64BIT)) {
guid = &tbl64[i].guid;
table = tbl64[i].table;
if (efi_enabled(EFI_64BIT)) { if (IS_ENABLED(CONFIG_X86_32) &&
u64 table64; tbl64[i].table > U32_MAX) {
guid = ((efi_config_table_64_t *)tablep)->guid;
table64 = ((efi_config_table_64_t *)tablep)->table;
table = table64;
#ifndef CONFIG_64BIT
if (table64 >> 32) {
pr_cont("\n"); pr_cont("\n");
pr_err("Table located above 4GB, disabling EFI.\n"); pr_err("Table located above 4GB, disabling EFI.\n");
return -EINVAL; return -EINVAL;
} }
#endif
} else { } else {
guid = ((efi_config_table_32_t *)tablep)->guid; guid = &tbl32[i].guid;
table = ((efi_config_table_32_t *)tablep)->table; table = tbl32[i].table;
} }
if (!match_config_table(&guid, table, common_tables)) if (!match_config_table(guid, table, common_tables))
match_config_table(&guid, table, arch_tables); match_config_table(guid, table, arch_tables);
tablep += sz;
} }
pr_cont("\n"); pr_cont("\n");
set_bit(EFI_CONFIG_TABLES, &efi.flags); set_bit(EFI_CONFIG_TABLES, &efi.flags);
if (efi.rng_seed != EFI_INVALID_TABLE_ADDR) { if (efi_rng_seed != EFI_INVALID_TABLE_ADDR) {
struct linux_efi_random_seed *seed; struct linux_efi_random_seed *seed;
u32 size = 0; u32 size = 0;
seed = early_memremap(efi.rng_seed, sizeof(*seed)); seed = early_memremap(efi_rng_seed, sizeof(*seed));
if (seed != NULL) { if (seed != NULL) {
size = READ_ONCE(seed->size); size = READ_ONCE(seed->size);
early_memunmap(seed, sizeof(*seed)); early_memunmap(seed, sizeof(*seed));
@ -615,7 +588,7 @@ int __init efi_config_parse_tables(void *config_tables, int count, int sz,
pr_err("Could not map UEFI random seed!\n"); pr_err("Could not map UEFI random seed!\n");
} }
if (size > 0) { if (size > 0) {
seed = early_memremap(efi.rng_seed, seed = early_memremap(efi_rng_seed,
sizeof(*seed) + size); sizeof(*seed) + size);
if (seed != NULL) { if (seed != NULL) {
pr_notice("seeding entropy pool\n"); pr_notice("seeding entropy pool\n");
@ -627,35 +600,17 @@ int __init efi_config_parse_tables(void *config_tables, int count, int sz,
} }
} }
if (efi_enabled(EFI_MEMMAP)) if (!IS_ENABLED(CONFIG_X86_32) && efi_enabled(EFI_MEMMAP))
efi_memattr_init(); efi_memattr_init();
efi_tpm_eventlog_init(); efi_tpm_eventlog_init();
/* Parse the EFI Properties table if it exists */ if (mem_reserve != EFI_INVALID_TABLE_ADDR) {
if (efi.properties_table != EFI_INVALID_TABLE_ADDR) { unsigned long prsv = mem_reserve;
efi_properties_table_t *tbl;
tbl = early_memremap(efi.properties_table, sizeof(*tbl));
if (tbl == NULL) {
pr_err("Could not map Properties table!\n");
return -ENOMEM;
}
if (tbl->memory_protection_attribute &
EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA)
set_bit(EFI_NX_PE_DATA, &efi.flags);
early_memunmap(tbl, sizeof(*tbl));
}
if (efi.mem_reserve != EFI_INVALID_TABLE_ADDR) {
unsigned long prsv = efi.mem_reserve;
while (prsv) { while (prsv) {
struct linux_efi_memreserve *rsv; struct linux_efi_memreserve *rsv;
u8 *p; u8 *p;
int i;
/* /*
* Just map a full page: that is what we will get * Just map a full page: that is what we will get
@ -684,187 +639,79 @@ int __init efi_config_parse_tables(void *config_tables, int count, int sz,
} }
} }
if (rt_prop != EFI_INVALID_TABLE_ADDR) {
efi_rt_properties_table_t *tbl;
tbl = early_memremap(rt_prop, sizeof(*tbl));
if (tbl) {
efi.runtime_supported_mask &= tbl->runtime_services_supported;
early_memunmap(tbl, sizeof(*tbl));
}
}
return 0; return 0;
} }
int __init efi_config_init(efi_config_table_type_t *arch_tables) int __init efi_systab_check_header(const efi_table_hdr_t *systab_hdr,
int min_major_version)
{ {
void *config_tables; if (systab_hdr->signature != EFI_SYSTEM_TABLE_SIGNATURE) {
int sz, ret; pr_err("System table signature incorrect!\n");
return -EINVAL;
if (efi.systab->nr_tables == 0)
return 0;
if (efi_enabled(EFI_64BIT))
sz = sizeof(efi_config_table_64_t);
else
sz = sizeof(efi_config_table_32_t);
/*
* Let's see what config tables the firmware passed to us.
*/
config_tables = early_memremap(efi.systab->tables,
efi.systab->nr_tables * sz);
if (config_tables == NULL) {
pr_err("Could not map Configuration table!\n");
return -ENOMEM;
} }
ret = efi_config_parse_tables(config_tables, efi.systab->nr_tables, sz, if ((systab_hdr->revision >> 16) < min_major_version)
arch_tables); pr_err("Warning: System table version %d.%02d, expected %d.00 or greater!\n",
systab_hdr->revision >> 16,
systab_hdr->revision & 0xffff,
min_major_version);
early_memunmap(config_tables, efi.systab->nr_tables * sz); return 0;
}
#ifndef CONFIG_IA64
static const efi_char16_t *__init map_fw_vendor(unsigned long fw_vendor,
size_t size)
{
const efi_char16_t *ret;
ret = early_memremap_ro(fw_vendor, size);
if (!ret)
pr_err("Could not map the firmware vendor!\n");
return ret; return ret;
} }
#ifdef CONFIG_EFI_VARS_MODULE static void __init unmap_fw_vendor(const void *fw_vendor, size_t size)
static int __init efi_load_efivars(void)
{ {
struct platform_device *pdev; early_memunmap((void *)fw_vendor, size);
if (!efi_enabled(EFI_RUNTIME_SERVICES))
return 0;
pdev = platform_device_register_simple("efivars", 0, NULL, 0);
return PTR_ERR_OR_ZERO(pdev);
} }
device_initcall(efi_load_efivars); #else
#define map_fw_vendor(p, s) __va(p)
#define unmap_fw_vendor(v, s)
#endif #endif
#ifdef CONFIG_EFI_PARAMS_FROM_FDT void __init efi_systab_report_header(const efi_table_hdr_t *systab_hdr,
unsigned long fw_vendor)
{
char vendor[100] = "unknown";
const efi_char16_t *c16;
size_t i;
#define UEFI_PARAM(name, prop, field) \ c16 = map_fw_vendor(fw_vendor, sizeof(vendor) * sizeof(efi_char16_t));
{ \ if (c16) {
{ name }, \ for (i = 0; i < sizeof(vendor) - 1 && c16[i]; ++i)
{ prop }, \ vendor[i] = c16[i];
offsetof(struct efi_fdt_params, field), \ vendor[i] = '\0';
sizeof_field(struct efi_fdt_params, field) \
unmap_fw_vendor(c16, sizeof(vendor) * sizeof(efi_char16_t));
} }
struct params { pr_info("EFI v%u.%.02u by %s\n",
const char name[32]; systab_hdr->revision >> 16,
const char propname[32]; systab_hdr->revision & 0xffff,
int offset; vendor);
int size;
};
static __initdata struct params fdt_params[] = {
UEFI_PARAM("System Table", "linux,uefi-system-table", system_table),
UEFI_PARAM("MemMap Address", "linux,uefi-mmap-start", mmap),
UEFI_PARAM("MemMap Size", "linux,uefi-mmap-size", mmap_size),
UEFI_PARAM("MemMap Desc. Size", "linux,uefi-mmap-desc-size", desc_size),
UEFI_PARAM("MemMap Desc. Version", "linux,uefi-mmap-desc-ver", desc_ver)
};
static __initdata struct params xen_fdt_params[] = {
UEFI_PARAM("System Table", "xen,uefi-system-table", system_table),
UEFI_PARAM("MemMap Address", "xen,uefi-mmap-start", mmap),
UEFI_PARAM("MemMap Size", "xen,uefi-mmap-size", mmap_size),
UEFI_PARAM("MemMap Desc. Size", "xen,uefi-mmap-desc-size", desc_size),
UEFI_PARAM("MemMap Desc. Version", "xen,uefi-mmap-desc-ver", desc_ver)
};
#define EFI_FDT_PARAMS_SIZE ARRAY_SIZE(fdt_params)
static __initdata struct {
const char *uname;
const char *subnode;
struct params *params;
} dt_params[] = {
{ "hypervisor", "uefi", xen_fdt_params },
{ "chosen", NULL, fdt_params },
};
struct param_info {
int found;
void *params;
const char *missing;
};
static int __init __find_uefi_params(unsigned long node,
struct param_info *info,
struct params *params)
{
const void *prop;
void *dest;
u64 val;
int i, len;
for (i = 0; i < EFI_FDT_PARAMS_SIZE; i++) {
prop = of_get_flat_dt_prop(node, params[i].propname, &len);
if (!prop) {
info->missing = params[i].name;
return 0;
}
dest = info->params + params[i].offset;
info->found++;
val = of_read_number(prop, len / sizeof(u32));
if (params[i].size == sizeof(u32))
*(u32 *)dest = val;
else
*(u64 *)dest = val;
if (efi_enabled(EFI_DBG))
pr_info(" %s: 0x%0*llx\n", params[i].name,
params[i].size * 2, val);
}
return 1;
} }
static int __init fdt_find_uefi_params(unsigned long node, const char *uname,
int depth, void *data)
{
struct param_info *info = data;
int i;
for (i = 0; i < ARRAY_SIZE(dt_params); i++) {
const char *subnode = dt_params[i].subnode;
if (depth != 1 || strcmp(uname, dt_params[i].uname) != 0) {
info->missing = dt_params[i].params[0].name;
continue;
}
if (subnode) {
int err = of_get_flat_dt_subnode_by_name(node, subnode);
if (err < 0)
return 0;
node = err;
}
return __find_uefi_params(node, info, dt_params[i].params);
}
return 0;
}
int __init efi_get_fdt_params(struct efi_fdt_params *params)
{
struct param_info info;
int ret;
pr_info("Getting EFI parameters from FDT:\n");
info.found = 0;
info.params = params;
ret = of_scan_flat_dt(fdt_find_uefi_params, &info);
if (!info.found)
pr_info("UEFI not found.\n");
else if (!ret)
pr_err("Can't find '%s' in device tree!\n",
info.missing);
return ret;
}
#endif /* CONFIG_EFI_PARAMS_FROM_FDT */
static __initdata char memory_type_name[][20] = { static __initdata char memory_type_name[][20] = {
"Reserved", "Reserved",
"Loader Code", "Loader Code",
@ -1025,10 +872,10 @@ static struct linux_efi_memreserve *efi_memreserve_root __ro_after_init;
static int __init efi_memreserve_map_root(void) static int __init efi_memreserve_map_root(void)
{ {
if (efi.mem_reserve == EFI_INVALID_TABLE_ADDR) if (mem_reserve == EFI_INVALID_TABLE_ADDR)
return -ENODEV; return -ENODEV;
efi_memreserve_root = memremap(efi.mem_reserve, efi_memreserve_root = memremap(mem_reserve,
sizeof(*efi_memreserve_root), sizeof(*efi_memreserve_root),
MEMREMAP_WB); MEMREMAP_WB);
if (WARN_ON_ONCE(!efi_memreserve_root)) if (WARN_ON_ONCE(!efi_memreserve_root))
@ -1133,7 +980,7 @@ static int update_efi_random_seed(struct notifier_block *nb,
if (!kexec_in_progress) if (!kexec_in_progress)
return NOTIFY_DONE; return NOTIFY_DONE;
seed = memremap(efi.rng_seed, sizeof(*seed), MEMREMAP_WB); seed = memremap(efi_rng_seed, sizeof(*seed), MEMREMAP_WB);
if (seed != NULL) { if (seed != NULL) {
size = min(seed->size, EFI_RANDOM_SEED_SIZE); size = min(seed->size, EFI_RANDOM_SEED_SIZE);
memunmap(seed); memunmap(seed);
@ -1141,7 +988,7 @@ static int update_efi_random_seed(struct notifier_block *nb,
pr_err("Could not map UEFI random seed!\n"); pr_err("Could not map UEFI random seed!\n");
} }
if (size > 0) { if (size > 0) {
seed = memremap(efi.rng_seed, sizeof(*seed) + size, seed = memremap(efi_rng_seed, sizeof(*seed) + size,
MEMREMAP_WB); MEMREMAP_WB);
if (seed != NULL) { if (seed != NULL) {
seed->size = size; seed->size = size;
@ -1158,9 +1005,9 @@ static struct notifier_block efi_random_seed_nb = {
.notifier_call = update_efi_random_seed, .notifier_call = update_efi_random_seed,
}; };
static int register_update_efi_random_seed(void) static int __init register_update_efi_random_seed(void)
{ {
if (efi.rng_seed == EFI_INVALID_TABLE_ADDR) if (efi_rng_seed == EFI_INVALID_TABLE_ADDR)
return 0; return 0;
return register_reboot_notifier(&efi_random_seed_nb); return register_reboot_notifier(&efi_random_seed_nb);
} }

View File

@ -678,7 +678,7 @@ int efivars_sysfs_init(void)
struct kobject *parent_kobj = efivars_kobject(); struct kobject *parent_kobj = efivars_kobject();
int error = 0; int error = 0;
if (!efi_enabled(EFI_RUNTIME_SERVICES)) if (!efi_rt_services_supported(EFI_RT_SUPPORTED_VARIABLE_SERVICES))
return -ENODEV; return -ENODEV;
/* No efivars has been registered yet */ /* No efivars has been registered yet */

View File

@ -240,7 +240,6 @@ void __init efi_esrt_init(void)
{ {
void *va; void *va;
struct efi_system_resource_table tmpesrt; struct efi_system_resource_table tmpesrt;
struct efi_system_resource_entry_v1 *v1_entries;
size_t size, max, entry_size, entries_size; size_t size, max, entry_size, entries_size;
efi_memory_desc_t md; efi_memory_desc_t md;
int rc; int rc;
@ -288,14 +287,13 @@ void __init efi_esrt_init(void)
memcpy(&tmpesrt, va, sizeof(tmpesrt)); memcpy(&tmpesrt, va, sizeof(tmpesrt));
early_memunmap(va, size); early_memunmap(va, size);
if (tmpesrt.fw_resource_version == 1) { if (tmpesrt.fw_resource_version != 1) {
entry_size = sizeof (*v1_entries);
} else {
pr_err("Unsupported ESRT version %lld.\n", pr_err("Unsupported ESRT version %lld.\n",
tmpesrt.fw_resource_version); tmpesrt.fw_resource_version);
return; return;
} }
entry_size = sizeof(struct efi_system_resource_entry_v1);
if (tmpesrt.fw_resource_count > 0 && max - size < entry_size) { if (tmpesrt.fw_resource_count > 0 && max - size < entry_size) {
pr_err("ESRT memory map entry can only hold the header. (max: %zu size: %zu)\n", pr_err("ESRT memory map entry can only hold the header. (max: %zu size: %zu)\n",
max - size, entry_size); max - size, entry_size);

View File

@ -0,0 +1,126 @@
// SPDX-License-Identifier: GPL-2.0-only
#define pr_fmt(fmt) "efi: " fmt
#include <linux/module.h>
#include <linux/init.h>
#include <linux/efi.h>
#include <linux/libfdt.h>
#include <linux/of_fdt.h>
#include <asm/unaligned.h>
enum {
SYSTAB,
MMBASE,
MMSIZE,
DCSIZE,
DCVERS,
PARAMCOUNT
};
static __initconst const char name[][22] = {
[SYSTAB] = "System Table ",
[MMBASE] = "MemMap Address ",
[MMSIZE] = "MemMap Size ",
[DCSIZE] = "MemMap Desc. Size ",
[DCVERS] = "MemMap Desc. Version ",
};
static __initconst const struct {
const char path[17];
const char params[PARAMCOUNT][26];
} dt_params[] = {
{
#ifdef CONFIG_XEN // <-------17------>
.path = "/hypervisor/uefi",
.params = {
[SYSTAB] = "xen,uefi-system-table",
[MMBASE] = "xen,uefi-mmap-start",
[MMSIZE] = "xen,uefi-mmap-size",
[DCSIZE] = "xen,uefi-mmap-desc-size",
[DCVERS] = "xen,uefi-mmap-desc-ver",
}
}, {
#endif
.path = "/chosen",
.params = { // <-----------26----------->
[SYSTAB] = "linux,uefi-system-table",
[MMBASE] = "linux,uefi-mmap-start",
[MMSIZE] = "linux,uefi-mmap-size",
[DCSIZE] = "linux,uefi-mmap-desc-size",
[DCVERS] = "linux,uefi-mmap-desc-ver",
}
}
};
static int __init efi_get_fdt_prop(const void *fdt, int node, const char *pname,
const char *rname, void *var, int size)
{
const void *prop;
int len;
u64 val;
prop = fdt_getprop(fdt, node, pname, &len);
if (!prop)
return 1;
val = (len == 4) ? (u64)be32_to_cpup(prop) : get_unaligned_be64(prop);
if (size == 8)
*(u64 *)var = val;
else
*(u32 *)var = (val < U32_MAX) ? val : U32_MAX; // saturate
if (efi_enabled(EFI_DBG))
pr_info(" %s: 0x%0*llx\n", rname, size * 2, val);
return 0;
}
u64 __init efi_get_fdt_params(struct efi_memory_map_data *mm)
{
const void *fdt = initial_boot_params;
unsigned long systab;
int i, j, node;
struct {
void *var;
int size;
} target[] = {
[SYSTAB] = { &systab, sizeof(systab) },
[MMBASE] = { &mm->phys_map, sizeof(mm->phys_map) },
[MMSIZE] = { &mm->size, sizeof(mm->size) },
[DCSIZE] = { &mm->desc_size, sizeof(mm->desc_size) },
[DCVERS] = { &mm->desc_version, sizeof(mm->desc_version) },
};
BUILD_BUG_ON(ARRAY_SIZE(target) != ARRAY_SIZE(name));
BUILD_BUG_ON(ARRAY_SIZE(target) != ARRAY_SIZE(dt_params[0].params));
for (i = 0; i < ARRAY_SIZE(dt_params); i++) {
node = fdt_path_offset(fdt, dt_params[i].path);
if (node < 0)
continue;
if (efi_enabled(EFI_DBG))
pr_info("Getting UEFI parameters from %s in DT:\n",
dt_params[i].path);
for (j = 0; j < ARRAY_SIZE(target); j++) {
const char *pname = dt_params[i].params[j];
if (!efi_get_fdt_prop(fdt, node, pname, name[j],
target[j].var, target[j].size))
continue;
if (!j)
goto notfound;
pr_err("Can't find property '%s' in DT!\n", pname);
return 0;
}
return systab;
}
notfound:
pr_info("UEFI not found.\n");
return 0;
}

View File

@ -25,6 +25,7 @@ cflags-$(CONFIG_ARM) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \
cflags-$(CONFIG_EFI_ARMSTUB) += -I$(srctree)/scripts/dtc/libfdt cflags-$(CONFIG_EFI_ARMSTUB) += -I$(srctree)/scripts/dtc/libfdt
KBUILD_CFLAGS := $(cflags-y) -DDISABLE_BRANCH_PROFILING \ KBUILD_CFLAGS := $(cflags-y) -DDISABLE_BRANCH_PROFILING \
-include $(srctree)/drivers/firmware/efi/libstub/hidden.h \
-D__NO_FORTIFY \ -D__NO_FORTIFY \
$(call cc-option,-ffreestanding) \ $(call cc-option,-ffreestanding) \
$(call cc-option,-fno-stack-protector) \ $(call cc-option,-fno-stack-protector) \
@ -39,11 +40,11 @@ OBJECT_FILES_NON_STANDARD := y
KCOV_INSTRUMENT := n KCOV_INSTRUMENT := n
lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \ lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \
random.o pci.o file.o mem.o random.o randomalloc.o pci.o \
skip_spaces.o lib-cmdline.o lib-ctype.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-y := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c arm-deps-y := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c
arm-deps-$(CONFIG_ARM64) += sort.c
$(obj)/lib-%.o: $(srctree)/lib/%.c FORCE $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE
$(call if_changed_rule,cc_o_c) $(call if_changed_rule,cc_o_c)
@ -53,6 +54,7 @@ lib-$(CONFIG_EFI_ARMSTUB) += arm-stub.o fdt.o string.o \
lib-$(CONFIG_ARM) += arm32-stub.o lib-$(CONFIG_ARM) += arm32-stub.o
lib-$(CONFIG_ARM64) += arm64-stub.o lib-$(CONFIG_ARM64) += arm64-stub.o
lib-$(CONFIG_X86) += x86-stub.o
CFLAGS_arm32-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) CFLAGS_arm32-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
CFLAGS_arm64-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) CFLAGS_arm64-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET)

View File

@ -10,7 +10,7 @@
*/ */
#include <linux/efi.h> #include <linux/efi.h>
#include <linux/sort.h> #include <linux/libfdt.h>
#include <asm/efi.h> #include <asm/efi.h>
#include "efistub.h" #include "efistub.h"
@ -36,6 +36,7 @@
#endif #endif
static u64 virtmap_base = EFI_RT_VIRTUAL_BASE; static u64 virtmap_base = EFI_RT_VIRTUAL_BASE;
static bool __efistub_global flat_va_mapping;
static efi_system_table_t *__efistub_global sys_table; static efi_system_table_t *__efistub_global sys_table;
@ -87,6 +88,39 @@ void install_memreserve_table(void)
pr_efi_err("Failed to install memreserve config table!\n"); pr_efi_err("Failed to install memreserve config table!\n");
} }
static unsigned long get_dram_base(void)
{
efi_status_t status;
unsigned long map_size, buff_size;
unsigned long membase = EFI_ERROR;
struct efi_memory_map map;
efi_memory_desc_t *md;
struct efi_boot_memmap boot_map;
boot_map.map = (efi_memory_desc_t **)&map.map;
boot_map.map_size = &map_size;
boot_map.desc_size = &map.desc_size;
boot_map.desc_ver = NULL;
boot_map.key_ptr = NULL;
boot_map.buff_size = &buff_size;
status = efi_get_memory_map(&boot_map);
if (status != EFI_SUCCESS)
return membase;
map.map_end = map.map + map_size;
for_each_efi_memory_desc_in_map(&map, md) {
if (md->attribute & EFI_MEMORY_WB) {
if (membase > md->phys_addr)
membase = md->phys_addr;
}
}
efi_bs_call(free_pool, map.map);
return membase;
}
/* /*
* This function handles the architcture specific differences between arm and * This function handles the architcture specific differences between arm and
@ -100,38 +134,46 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
unsigned long *reserve_size, unsigned long *reserve_size,
unsigned long dram_base, unsigned long dram_base,
efi_loaded_image_t *image); efi_loaded_image_t *image);
asmlinkage void __noreturn efi_enter_kernel(unsigned long entrypoint,
unsigned long fdt_addr,
unsigned long fdt_size);
/* /*
* EFI entry point for the arm/arm64 EFI stubs. This is the entrypoint * EFI entry point for the arm/arm64 EFI stubs. This is the entrypoint
* that is described in the PE/COFF header. Most of the code is the same * that is described in the PE/COFF header. Most of the code is the same
* for both archictectures, with the arch-specific code provided in the * for both archictectures, with the arch-specific code provided in the
* handle_kernel_image() function. * handle_kernel_image() function.
*/ */
unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg, efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
unsigned long *image_addr)
{ {
efi_loaded_image_t *image; efi_loaded_image_t *image;
efi_status_t status; efi_status_t status;
unsigned long image_addr;
unsigned long image_size = 0; unsigned long image_size = 0;
unsigned long dram_base; unsigned long dram_base;
/* addr/point and size pairs for memory management*/ /* addr/point and size pairs for memory management*/
unsigned long initrd_addr; unsigned long initrd_addr = 0;
u64 initrd_size = 0; unsigned long initrd_size = 0;
unsigned long fdt_addr = 0; /* Original DTB */ unsigned long fdt_addr = 0; /* Original DTB */
unsigned long fdt_size = 0; unsigned long fdt_size = 0;
char *cmdline_ptr = NULL; char *cmdline_ptr = NULL;
int cmdline_size = 0; int cmdline_size = 0;
unsigned long new_fdt_addr;
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;
enum efi_secureboot_mode secure_boot; enum efi_secureboot_mode secure_boot;
struct screen_info *si; struct screen_info *si;
efi_properties_table_t *prop_tbl;
unsigned long max_addr;
sys_table = sys_table_arg; sys_table = sys_table_arg;
/* 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) {
status = EFI_INVALID_PARAMETER;
goto fail; goto fail;
}
status = check_platform_features(); status = check_platform_features();
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
@ -152,6 +194,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
dram_base = get_dram_base(); dram_base = get_dram_base();
if (dram_base == EFI_ERROR) { if (dram_base == EFI_ERROR) {
pr_efi_err("Failed to find DRAM base\n"); pr_efi_err("Failed to find DRAM base\n");
status = EFI_LOAD_ERROR;
goto fail; goto fail;
} }
@ -160,9 +203,10 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
* protocol. We are going to copy the command line into the * protocol. We are going to copy the command line into the
* device tree, so this can be allocated anywhere. * device tree, so this can be allocated anywhere.
*/ */
cmdline_ptr = efi_convert_cmdline(image, &cmdline_size); cmdline_ptr = efi_convert_cmdline(image, &cmdline_size, ULONG_MAX);
if (!cmdline_ptr) { if (!cmdline_ptr) {
pr_efi_err("getting command line via LOADED_IMAGE_PROTOCOL\n"); pr_efi_err("getting command line via LOADED_IMAGE_PROTOCOL\n");
status = EFI_OUT_OF_RESOURCES;
goto fail; goto fail;
} }
@ -178,7 +222,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
si = setup_graphics(); si = setup_graphics();
status = handle_kernel_image(image_addr, &image_size, status = handle_kernel_image(&image_addr, &image_size,
&reserve_addr, &reserve_addr,
&reserve_size, &reserve_size,
dram_base, image); dram_base, image);
@ -204,8 +248,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
if (strstr(cmdline_ptr, "dtb=")) if (strstr(cmdline_ptr, "dtb="))
pr_efi("Ignoring DTB from command line.\n"); pr_efi("Ignoring DTB from command line.\n");
} else { } else {
status = handle_cmdline_files(image, cmdline_ptr, "dtb=", status = efi_load_dtb(image, &fdt_addr, &fdt_size);
~0UL, &fdt_addr, &fdt_size);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
pr_efi_err("Failed to load device tree!\n"); pr_efi_err("Failed to load device tree!\n");
@ -225,18 +268,38 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
if (!fdt_addr) if (!fdt_addr)
pr_efi("Generating empty DTB\n"); pr_efi("Generating empty DTB\n");
status = handle_cmdline_files(image, cmdline_ptr, "initrd=", if (!noinitrd()) {
efi_get_max_initrd_addr(dram_base, max_addr = efi_get_max_initrd_addr(dram_base, image_addr);
*image_addr), status = efi_load_initrd_dev_path(&initrd_addr, &initrd_size,
(unsigned long *)&initrd_addr, max_addr);
(unsigned long *)&initrd_size); if (status == EFI_SUCCESS) {
if (status != EFI_SUCCESS) pr_efi("Loaded initrd from LINUX_EFI_INITRD_MEDIA_GUID device path\n");
pr_efi_err("Failed initrd from command line!\n"); } else if (status == EFI_NOT_FOUND) {
status = efi_load_initrd(image, &initrd_addr, &initrd_size,
ULONG_MAX, max_addr);
if (status == EFI_SUCCESS && initrd_size > 0)
pr_efi("Loaded initrd from command line option\n");
}
if (status != EFI_SUCCESS)
pr_efi_err("Failed to load initrd!\n");
}
efi_random_get_seed(); efi_random_get_seed();
/*
* If the NX PE data feature is enabled in the properties table, we
* should take care not to create a virtual mapping that changes the
* relative placement of runtime services code and data regions, as
* they may belong to the same PE/COFF executable image in memory.
* The easiest way to achieve that is to simply use a 1:1 mapping.
*/
prop_tbl = get_efi_config_table(EFI_PROPERTIES_TABLE_GUID);
flat_va_mapping = prop_tbl &&
(prop_tbl->memory_protection_attribute &
EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA);
/* hibernation expects the runtime regions to stay in the same place */ /* hibernation expects the runtime regions to stay in the same place */
if (!IS_ENABLED(CONFIG_HIBERNATION) && !nokaslr()) { if (!IS_ENABLED(CONFIG_HIBERNATION) && !nokaslr() && !flat_va_mapping) {
/* /*
* Randomize the base of the UEFI runtime services region. * Randomize the base of the UEFI runtime services region.
* Preserve the 2 MB alignment of the region by taking a * Preserve the 2 MB alignment of the region by taking a
@ -257,71 +320,30 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
install_memreserve_table(); install_memreserve_table();
new_fdt_addr = fdt_addr; status = allocate_new_fdt_and_exit_boot(handle, &fdt_addr,
status = allocate_new_fdt_and_exit_boot(handle, efi_get_max_fdt_addr(dram_base),
&new_fdt_addr, efi_get_max_fdt_addr(dram_base), initrd_addr, initrd_size,
initrd_addr, initrd_size, cmdline_ptr, cmdline_ptr, fdt_addr, fdt_size);
fdt_addr, fdt_size); if (status != EFI_SUCCESS)
goto fail_free_initrd;
/* efi_enter_kernel(image_addr, fdt_addr, fdt_totalsize((void *)fdt_addr));
* If all went well, we need to return the FDT address to the /* not reached */
* calling function so it can be passed to kernel as part of
* the kernel boot protocol.
*/
if (status == EFI_SUCCESS)
return new_fdt_addr;
fail_free_initrd:
pr_efi_err("Failed to update FDT and exit boot services\n"); pr_efi_err("Failed to update FDT and exit boot services\n");
efi_free(initrd_size, initrd_addr); efi_free(initrd_size, initrd_addr);
efi_free(fdt_size, fdt_addr); efi_free(fdt_size, fdt_addr);
fail_free_image: fail_free_image:
efi_free(image_size, *image_addr); efi_free(image_size, image_addr);
efi_free(reserve_size, reserve_addr); efi_free(reserve_size, reserve_addr);
fail_free_cmdline: fail_free_cmdline:
free_screen_info(si); free_screen_info(si);
efi_free(cmdline_size, (unsigned long)cmdline_ptr); efi_free(cmdline_size, (unsigned long)cmdline_ptr);
fail: fail:
return EFI_ERROR; return status;
}
static int cmp_mem_desc(const void *l, const void *r)
{
const efi_memory_desc_t *left = l, *right = r;
return (left->phys_addr > right->phys_addr) ? 1 : -1;
}
/*
* Returns whether region @left ends exactly where region @right starts,
* or false if either argument is NULL.
*/
static bool regions_are_adjacent(efi_memory_desc_t *left,
efi_memory_desc_t *right)
{
u64 left_end;
if (left == NULL || right == NULL)
return false;
left_end = left->phys_addr + left->num_pages * EFI_PAGE_SIZE;
return left_end == right->phys_addr;
}
/*
* Returns whether region @left and region @right have compatible memory type
* mapping attributes, and are both EFI_MEMORY_RUNTIME regions.
*/
static bool regions_have_compatible_memory_type_attrs(efi_memory_desc_t *left,
efi_memory_desc_t *right)
{
static const u64 mem_type_mask = EFI_MEMORY_WB | EFI_MEMORY_WT |
EFI_MEMORY_WC | EFI_MEMORY_UC |
EFI_MEMORY_RUNTIME;
return ((left->attribute ^ right->attribute) & mem_type_mask) == 0;
} }
/* /*
@ -336,23 +358,10 @@ void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size,
int *count) int *count)
{ {
u64 efi_virt_base = virtmap_base; u64 efi_virt_base = virtmap_base;
efi_memory_desc_t *in, *prev = NULL, *out = runtime_map; efi_memory_desc_t *in, *out = runtime_map;
int l; int l;
/* for (l = 0; l < map_size; l += desc_size) {
* To work around potential issues with the Properties Table feature
* introduced in UEFI 2.5, which may split PE/COFF executable images
* in memory into several RuntimeServicesCode and RuntimeServicesData
* regions, we need to preserve the relative offsets between adjacent
* EFI_MEMORY_RUNTIME regions with the same memory type attributes.
* The easiest way to find adjacent regions is to sort the memory map
* before traversing it.
*/
if (IS_ENABLED(CONFIG_ARM64))
sort(memory_map, map_size / desc_size, desc_size, cmp_mem_desc,
NULL);
for (l = 0; l < map_size; l += desc_size, prev = in) {
u64 paddr, size; u64 paddr, size;
in = (void *)memory_map + l; in = (void *)memory_map + l;
@ -362,8 +371,8 @@ void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size,
paddr = in->phys_addr; paddr = in->phys_addr;
size = in->num_pages * EFI_PAGE_SIZE; size = in->num_pages * EFI_PAGE_SIZE;
in->virt_addr = in->phys_addr;
if (novamap()) { if (novamap()) {
in->virt_addr = in->phys_addr;
continue; continue;
} }
@ -372,9 +381,7 @@ void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size,
* a 4k page size kernel to kexec a 64k page size kernel and * a 4k page size kernel to kexec a 64k page size kernel and
* vice versa. * vice versa.
*/ */
if ((IS_ENABLED(CONFIG_ARM64) && if (!flat_va_mapping) {
!regions_are_adjacent(prev, in)) ||
!regions_have_compatible_memory_type_attrs(prev, in)) {
paddr = round_down(in->phys_addr, SZ_64K); paddr = round_down(in->phys_addr, SZ_64K);
size += in->phys_addr - paddr; size += in->phys_addr - paddr;
@ -389,10 +396,10 @@ void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size,
efi_virt_base = round_up(efi_virt_base, SZ_2M); efi_virt_base = round_up(efi_virt_base, SZ_2M);
else else
efi_virt_base = round_up(efi_virt_base, SZ_64K); efi_virt_base = round_up(efi_virt_base, SZ_64K);
}
in->virt_addr = efi_virt_base + in->phys_addr - paddr; in->virt_addr += efi_virt_base - paddr;
efi_virt_base += size; efi_virt_base += size;
}
memcpy(out, in, desc_size); memcpy(out, in, desc_size);
out = (void *)out + desc_size; out = (void *)out + desc_size;

View File

@ -227,6 +227,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
* Relocate the zImage, so that it appears in the lowest 128 MB * Relocate the zImage, so that it appears in the lowest 128 MB
* memory window. * memory window.
*/ */
*image_addr = (unsigned long)image->image_base;
*image_size = image->image_size; *image_size = image->image_size;
status = efi_relocate_kernel(image_addr, *image_size, *image_size, status = efi_relocate_kernel(image_addr, *image_size, *image_size,
kernel_base + MAX_UNCOMP_KERNEL_SIZE, 0, 0); kernel_base + MAX_UNCOMP_KERNEL_SIZE, 0, 0);

View File

@ -6,17 +6,11 @@
* Adapted from ARM version by Mark Salter <msalter@redhat.com> * Adapted from ARM version by Mark Salter <msalter@redhat.com>
*/ */
/*
* To prevent the compiler from emitting GOT-indirected (and thus absolute)
* references to the section markers, override their visibility as 'hidden'
*/
#pragma GCC visibility push(hidden)
#include <asm/sections.h>
#pragma GCC visibility pop
#include <linux/efi.h> #include <linux/efi.h>
#include <asm/efi.h> #include <asm/efi.h>
#include <asm/memory.h> #include <asm/memory.h>
#include <asm/sections.h>
#include <asm/sysreg.h> #include <asm/sysreg.h>
#include "efistub.h" #include "efistub.h"
@ -49,7 +43,6 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
{ {
efi_status_t status; efi_status_t status;
unsigned long kernel_size, kernel_memsize = 0; unsigned long kernel_size, kernel_memsize = 0;
void *old_image_addr = (void *)*image_addr;
unsigned long preferred_offset; unsigned long preferred_offset;
u64 phys_seed = 0; u64 phys_seed = 0;
@ -123,6 +116,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
* Mustang), we can still place the kernel at the address * Mustang), we can still place the kernel at the address
* 'dram_base + TEXT_OFFSET'. * 'dram_base + TEXT_OFFSET'.
*/ */
*image_addr = (unsigned long)_text;
if (*image_addr == preferred_offset) if (*image_addr == preferred_offset)
return EFI_SUCCESS; return EFI_SUCCESS;
@ -147,7 +141,11 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
} }
*image_addr = *reserve_addr + TEXT_OFFSET; *image_addr = *reserve_addr + TEXT_OFFSET;
} }
memcpy((void *)*image_addr, old_image_addr, kernel_size);
if (image->image_base != _text)
pr_efi_err("FIRMWARE BUG: efi_loaded_image_t::image_base has bogus value\n");
memcpy((void *)*image_addr, _text, kernel_size);
return EFI_SUCCESS; return EFI_SUCCESS;
} }

View File

@ -12,34 +12,27 @@
#include "efistub.h" #include "efistub.h"
/* static bool __efistub_global efi_nochunk;
* Some firmware implementations have problems reading files in one go.
* A read chunk size of 1MB seems to work for most platforms.
*
* Unfortunately, reading files in chunks triggers *other* bugs on some
* platforms, so we provide a way to disable this workaround, which can
* be done by passing "efi=nochunk" on the EFI boot stub command line.
*
* If you experience issues with initrd images being corrupt it's worth
* trying efi=nochunk, but chunking is enabled by default because there
* are far more machines that require the workaround than those that
* break with it enabled.
*/
#define EFI_READ_CHUNK_SIZE (1024 * 1024)
static unsigned long efi_chunk_size = EFI_READ_CHUNK_SIZE;
static bool __efistub_global efi_nokaslr; static bool __efistub_global efi_nokaslr;
static bool __efistub_global efi_noinitrd;
static bool __efistub_global efi_quiet; static bool __efistub_global efi_quiet;
static bool __efistub_global efi_novamap; static bool __efistub_global efi_novamap;
static bool __efistub_global efi_nosoftreserve; static bool __efistub_global efi_nosoftreserve;
static bool __efistub_global efi_disable_pci_dma = static bool __efistub_global efi_disable_pci_dma =
IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA); IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA);
bool __pure nochunk(void)
{
return efi_nochunk;
}
bool __pure nokaslr(void) bool __pure nokaslr(void)
{ {
return efi_nokaslr; return efi_nokaslr;
} }
bool __pure noinitrd(void)
{
return efi_noinitrd;
}
bool __pure is_quiet(void) bool __pure is_quiet(void)
{ {
return efi_quiet; return efi_quiet;
@ -53,13 +46,6 @@ bool __pure __efi_soft_reserve_enabled(void)
return !efi_nosoftreserve; return !efi_nosoftreserve;
} }
#define EFI_MMAP_NR_SLACK_SLOTS 8
struct file_info {
efi_file_handle_t *handle;
u64 size;
};
void efi_printk(char *str) void efi_printk(char *str)
{ {
char *s8; char *s8;
@ -77,369 +63,6 @@ void efi_printk(char *str)
} }
} }
static inline bool mmap_has_headroom(unsigned long buff_size,
unsigned long map_size,
unsigned long desc_size)
{
unsigned long slack = buff_size - map_size;
return slack / desc_size >= EFI_MMAP_NR_SLACK_SLOTS;
}
efi_status_t efi_get_memory_map(struct efi_boot_memmap *map)
{
efi_memory_desc_t *m = NULL;
efi_status_t status;
unsigned long key;
u32 desc_version;
*map->desc_size = sizeof(*m);
*map->map_size = *map->desc_size * 32;
*map->buff_size = *map->map_size;
again:
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
*map->map_size, (void **)&m);
if (status != EFI_SUCCESS)
goto fail;
*map->desc_size = 0;
key = 0;
status = efi_bs_call(get_memory_map, map->map_size, m,
&key, map->desc_size, &desc_version);
if (status == EFI_BUFFER_TOO_SMALL ||
!mmap_has_headroom(*map->buff_size, *map->map_size,
*map->desc_size)) {
efi_bs_call(free_pool, m);
/*
* Make sure there is some entries of headroom so that the
* buffer can be reused for a new map after allocations are
* no longer permitted. Its unlikely that the map will grow to
* exceed this headroom once we are ready to trigger
* ExitBootServices()
*/
*map->map_size += *map->desc_size * EFI_MMAP_NR_SLACK_SLOTS;
*map->buff_size = *map->map_size;
goto again;
}
if (status != EFI_SUCCESS)
efi_bs_call(free_pool, m);
if (map->key_ptr && status == EFI_SUCCESS)
*map->key_ptr = key;
if (map->desc_ver && status == EFI_SUCCESS)
*map->desc_ver = desc_version;
fail:
*map->map = m;
return status;
}
unsigned long get_dram_base(void)
{
efi_status_t status;
unsigned long map_size, buff_size;
unsigned long membase = EFI_ERROR;
struct efi_memory_map map;
efi_memory_desc_t *md;
struct efi_boot_memmap boot_map;
boot_map.map = (efi_memory_desc_t **)&map.map;
boot_map.map_size = &map_size;
boot_map.desc_size = &map.desc_size;
boot_map.desc_ver = NULL;
boot_map.key_ptr = NULL;
boot_map.buff_size = &buff_size;
status = efi_get_memory_map(&boot_map);
if (status != EFI_SUCCESS)
return membase;
map.map_end = map.map + map_size;
for_each_efi_memory_desc_in_map(&map, md) {
if (md->attribute & EFI_MEMORY_WB) {
if (membase > md->phys_addr)
membase = md->phys_addr;
}
}
efi_bs_call(free_pool, map.map);
return membase;
}
/*
* Allocate at the highest possible address that is not above 'max'.
*/
efi_status_t efi_high_alloc(unsigned long size, unsigned long align,
unsigned long *addr, unsigned long max)
{
unsigned long map_size, desc_size, buff_size;
efi_memory_desc_t *map;
efi_status_t status;
unsigned long nr_pages;
u64 max_addr = 0;
int i;
struct efi_boot_memmap boot_map;
boot_map.map = &map;
boot_map.map_size = &map_size;
boot_map.desc_size = &desc_size;
boot_map.desc_ver = NULL;
boot_map.key_ptr = NULL;
boot_map.buff_size = &buff_size;
status = efi_get_memory_map(&boot_map);
if (status != EFI_SUCCESS)
goto fail;
/*
* Enforce minimum alignment that EFI or Linux requires when
* requesting a specific address. We are doing page-based (or
* larger) allocations, and both the address and size must meet
* alignment constraints.
*/
if (align < EFI_ALLOC_ALIGN)
align = EFI_ALLOC_ALIGN;
size = round_up(size, EFI_ALLOC_ALIGN);
nr_pages = size / EFI_PAGE_SIZE;
again:
for (i = 0; i < map_size / desc_size; i++) {
efi_memory_desc_t *desc;
unsigned long m = (unsigned long)map;
u64 start, end;
desc = efi_early_memdesc_ptr(m, desc_size, i);
if (desc->type != EFI_CONVENTIONAL_MEMORY)
continue;
if (efi_soft_reserve_enabled() &&
(desc->attribute & EFI_MEMORY_SP))
continue;
if (desc->num_pages < nr_pages)
continue;
start = desc->phys_addr;
end = start + desc->num_pages * EFI_PAGE_SIZE;
if (end > max)
end = max;
if ((start + size) > end)
continue;
if (round_down(end - size, align) < start)
continue;
start = round_down(end - size, align);
/*
* Don't allocate at 0x0. It will confuse code that
* checks pointers against NULL.
*/
if (start == 0x0)
continue;
if (start > max_addr)
max_addr = start;
}
if (!max_addr)
status = EFI_NOT_FOUND;
else {
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
EFI_LOADER_DATA, nr_pages, &max_addr);
if (status != EFI_SUCCESS) {
max = max_addr;
max_addr = 0;
goto again;
}
*addr = max_addr;
}
efi_bs_call(free_pool, map);
fail:
return status;
}
/*
* Allocate at the lowest possible address that is not below 'min'.
*/
efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align,
unsigned long *addr, unsigned long min)
{
unsigned long map_size, desc_size, buff_size;
efi_memory_desc_t *map;
efi_status_t status;
unsigned long nr_pages;
int i;
struct efi_boot_memmap boot_map;
boot_map.map = &map;
boot_map.map_size = &map_size;
boot_map.desc_size = &desc_size;
boot_map.desc_ver = NULL;
boot_map.key_ptr = NULL;
boot_map.buff_size = &buff_size;
status = efi_get_memory_map(&boot_map);
if (status != EFI_SUCCESS)
goto fail;
/*
* Enforce minimum alignment that EFI or Linux requires when
* requesting a specific address. We are doing page-based (or
* larger) allocations, and both the address and size must meet
* alignment constraints.
*/
if (align < EFI_ALLOC_ALIGN)
align = EFI_ALLOC_ALIGN;
size = round_up(size, EFI_ALLOC_ALIGN);
nr_pages = size / EFI_PAGE_SIZE;
for (i = 0; i < map_size / desc_size; i++) {
efi_memory_desc_t *desc;
unsigned long m = (unsigned long)map;
u64 start, end;
desc = efi_early_memdesc_ptr(m, desc_size, i);
if (desc->type != EFI_CONVENTIONAL_MEMORY)
continue;
if (efi_soft_reserve_enabled() &&
(desc->attribute & EFI_MEMORY_SP))
continue;
if (desc->num_pages < nr_pages)
continue;
start = desc->phys_addr;
end = start + desc->num_pages * EFI_PAGE_SIZE;
if (start < min)
start = min;
start = round_up(start, align);
if ((start + size) > end)
continue;
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
EFI_LOADER_DATA, nr_pages, &start);
if (status == EFI_SUCCESS) {
*addr = start;
break;
}
}
if (i == map_size / desc_size)
status = EFI_NOT_FOUND;
efi_bs_call(free_pool, map);
fail:
return status;
}
void efi_free(unsigned long size, unsigned long addr)
{
unsigned long nr_pages;
if (!size)
return;
nr_pages = round_up(size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
efi_bs_call(free_pages, addr, nr_pages);
}
static efi_status_t efi_file_size(void *__fh, efi_char16_t *filename_16,
void **handle, u64 *file_sz)
{
efi_file_handle_t *h, *fh = __fh;
efi_file_info_t *info;
efi_status_t status;
efi_guid_t info_guid = EFI_FILE_INFO_ID;
unsigned long info_sz;
status = fh->open(fh, &h, filename_16, EFI_FILE_MODE_READ, 0);
if (status != EFI_SUCCESS) {
efi_printk("Failed to open file: ");
efi_char16_printk(filename_16);
efi_printk("\n");
return status;
}
*handle = h;
info_sz = 0;
status = h->get_info(h, &info_guid, &info_sz, NULL);
if (status != EFI_BUFFER_TOO_SMALL) {
efi_printk("Failed to get file info size\n");
return status;
}
grow:
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, info_sz,
(void **)&info);
if (status != EFI_SUCCESS) {
efi_printk("Failed to alloc mem for file info\n");
return status;
}
status = h->get_info(h, &info_guid, &info_sz, info);
if (status == EFI_BUFFER_TOO_SMALL) {
efi_bs_call(free_pool, info);
goto grow;
}
*file_sz = info->file_size;
efi_bs_call(free_pool, info);
if (status != EFI_SUCCESS)
efi_printk("Failed to get initrd info\n");
return status;
}
static efi_status_t efi_file_read(efi_file_handle_t *handle,
unsigned long *size, void *addr)
{
return handle->read(handle, size, addr);
}
static efi_status_t efi_file_close(efi_file_handle_t *handle)
{
return handle->close(handle);
}
static efi_status_t efi_open_volume(efi_loaded_image_t *image,
efi_file_handle_t **__fh)
{
efi_file_io_interface_t *io;
efi_file_handle_t *fh;
efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID;
efi_status_t status;
efi_handle_t handle = image->device_handle;
status = efi_bs_call(handle_protocol, handle, &fs_proto, (void **)&io);
if (status != EFI_SUCCESS) {
efi_printk("Failed to handle fs_proto\n");
return status;
}
status = io->open_volume(io, &fh);
if (status != EFI_SUCCESS)
efi_printk("Failed to open volume\n");
else
*__fh = fh;
return status;
}
/* /*
* Parse the ASCII string 'cmdline' for EFI options, denoted by the efi= * Parse the ASCII string 'cmdline' for EFI options, denoted by the efi=
* option, e.g. efi=nochunk. * option, e.g. efi=nochunk.
@ -450,316 +73,42 @@ static efi_status_t efi_open_volume(efi_loaded_image_t *image,
*/ */
efi_status_t efi_parse_options(char const *cmdline) efi_status_t efi_parse_options(char const *cmdline)
{ {
char *str; size_t len = strlen(cmdline) + 1;
str = strstr(cmdline, "nokaslr");
if (str == cmdline || (str && str > cmdline && *(str - 1) == ' '))
efi_nokaslr = true;
str = strstr(cmdline, "quiet");
if (str == cmdline || (str && str > cmdline && *(str - 1) == ' '))
efi_quiet = true;
/*
* If no EFI parameters were specified on the cmdline we've got
* nothing to do.
*/
str = strstr(cmdline, "efi=");
if (!str)
return EFI_SUCCESS;
/* Skip ahead to first argument */
str += strlen("efi=");
/*
* Remember, because efi= is also used by the kernel we need to
* skip over arguments we don't understand.
*/
while (*str && *str != ' ') {
if (!strncmp(str, "nochunk", 7)) {
str += strlen("nochunk");
efi_chunk_size = -1UL;
}
if (!strncmp(str, "novamap", 7)) {
str += strlen("novamap");
efi_novamap = true;
}
if (IS_ENABLED(CONFIG_EFI_SOFT_RESERVE) &&
!strncmp(str, "nosoftreserve", 7)) {
str += strlen("nosoftreserve");
efi_nosoftreserve = true;
}
if (!strncmp(str, "disable_early_pci_dma", 21)) {
str += strlen("disable_early_pci_dma");
efi_disable_pci_dma = true;
}
if (!strncmp(str, "no_disable_early_pci_dma", 24)) {
str += strlen("no_disable_early_pci_dma");
efi_disable_pci_dma = false;
}
/* Group words together, delimited by "," */
while (*str && *str != ' ' && *str != ',')
str++;
if (*str == ',')
str++;
}
return EFI_SUCCESS;
}
/*
* Check the cmdline for a LILO-style file= arguments.
*
* We only support loading a file from the same filesystem as
* the kernel image.
*/
efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
char *cmd_line, char *option_string,
unsigned long max_addr,
unsigned long *load_addr,
unsigned long *load_size)
{
struct file_info *files;
unsigned long file_addr;
u64 file_size_total;
efi_file_handle_t *fh = NULL;
efi_status_t status; efi_status_t status;
int nr_files; char *str, *buf;
char *str;
int i, j, k;
file_addr = 0; status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, len, (void **)&buf);
file_size_total = 0; if (status != EFI_SUCCESS)
str = cmd_line;
j = 0; /* See close_handles */
if (!load_addr || !load_size)
return EFI_INVALID_PARAMETER;
*load_addr = 0;
*load_size = 0;
if (!str || !*str)
return EFI_SUCCESS;
for (nr_files = 0; *str; nr_files++) {
str = strstr(str, option_string);
if (!str)
break;
str += strlen(option_string);
/* Skip any leading slashes */
while (*str == '/' || *str == '\\')
str++;
while (*str && *str != ' ' && *str != '\n')
str++;
}
if (!nr_files)
return EFI_SUCCESS;
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
nr_files * sizeof(*files), (void **)&files);
if (status != EFI_SUCCESS) {
pr_efi_err("Failed to alloc mem for file handle list\n");
goto fail;
}
str = cmd_line;
for (i = 0; i < nr_files; i++) {
struct file_info *file;
efi_char16_t filename_16[256];
efi_char16_t *p;
str = strstr(str, option_string);
if (!str)
break;
str += strlen(option_string);
file = &files[i];
p = filename_16;
/* Skip any leading slashes */
while (*str == '/' || *str == '\\')
str++;
while (*str && *str != ' ' && *str != '\n') {
if ((u8 *)p >= (u8 *)filename_16 + sizeof(filename_16))
break;
if (*str == '/') {
*p++ = '\\';
str++;
} else {
*p++ = *str++;
}
}
*p = '\0';
/* Only open the volume once. */
if (!i) {
status = efi_open_volume(image, &fh);
if (status != EFI_SUCCESS)
goto free_files;
}
status = efi_file_size(fh, filename_16, (void **)&file->handle,
&file->size);
if (status != EFI_SUCCESS)
goto close_handles;
file_size_total += file->size;
}
if (file_size_total) {
unsigned long addr;
/*
* Multiple files need to be at consecutive addresses in memory,
* so allocate enough memory for all the files. This is used
* for loading multiple files.
*/
status = efi_high_alloc(file_size_total, 0x1000, &file_addr,
max_addr);
if (status != EFI_SUCCESS) {
pr_efi_err("Failed to alloc highmem for files\n");
goto close_handles;
}
/* We've run out of free low memory. */
if (file_addr > max_addr) {
pr_efi_err("We've run out of free low memory\n");
status = EFI_INVALID_PARAMETER;
goto free_file_total;
}
addr = file_addr;
for (j = 0; j < nr_files; j++) {
unsigned long size;
size = files[j].size;
while (size) {
unsigned long chunksize;
if (IS_ENABLED(CONFIG_X86) && size > efi_chunk_size)
chunksize = efi_chunk_size;
else
chunksize = size;
status = efi_file_read(files[j].handle,
&chunksize,
(void *)addr);
if (status != EFI_SUCCESS) {
pr_efi_err("Failed to read file\n");
goto free_file_total;
}
addr += chunksize;
size -= chunksize;
}
efi_file_close(files[j].handle);
}
}
efi_bs_call(free_pool, files);
*load_addr = file_addr;
*load_size = file_size_total;
return status;
free_file_total:
efi_free(file_size_total, file_addr);
close_handles:
for (k = j; k < i; k++)
efi_file_close(files[k].handle);
free_files:
efi_bs_call(free_pool, files);
fail:
*load_addr = 0;
*load_size = 0;
return status;
}
/*
* Relocate a kernel image, either compressed or uncompressed.
* In the ARM64 case, all kernel images are currently
* uncompressed, and as such when we relocate it we need to
* allocate additional space for the BSS segment. Any low
* memory that this function should avoid needs to be
* unavailable in the EFI memory map, as if the preferred
* address is not available the lowest available address will
* be used.
*/
efi_status_t efi_relocate_kernel(unsigned long *image_addr,
unsigned long image_size,
unsigned long alloc_size,
unsigned long preferred_addr,
unsigned long alignment,
unsigned long min_addr)
{
unsigned long cur_image_addr;
unsigned long new_addr = 0;
efi_status_t status;
unsigned long nr_pages;
efi_physical_addr_t efi_addr = preferred_addr;
if (!image_addr || !image_size || !alloc_size)
return EFI_INVALID_PARAMETER;
if (alloc_size < image_size)
return EFI_INVALID_PARAMETER;
cur_image_addr = *image_addr;
/*
* The EFI firmware loader could have placed the kernel image
* anywhere in memory, but the kernel has restrictions on the
* max physical address it can run at. Some architectures
* also have a prefered address, so first try to relocate
* to the preferred address. If that fails, allocate as low
* as possible while respecting the required alignment.
*/
nr_pages = round_up(alloc_size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
EFI_LOADER_DATA, nr_pages, &efi_addr);
new_addr = efi_addr;
/*
* If preferred address allocation failed allocate as low as
* possible.
*/
if (status != EFI_SUCCESS) {
status = efi_low_alloc_above(alloc_size, alignment, &new_addr,
min_addr);
}
if (status != EFI_SUCCESS) {
pr_efi_err("Failed to allocate usable memory for kernel.\n");
return status; return status;
str = skip_spaces(memcpy(buf, cmdline, len));
while (*str) {
char *param, *val;
str = next_arg(str, &param, &val);
if (!strcmp(param, "nokaslr")) {
efi_nokaslr = true;
} else if (!strcmp(param, "quiet")) {
efi_quiet = true;
} else if (!strcmp(param, "noinitrd")) {
efi_noinitrd = true;
} else if (!strcmp(param, "efi") && val) {
efi_nochunk = parse_option_str(val, "nochunk");
efi_novamap = parse_option_str(val, "novamap");
efi_nosoftreserve = IS_ENABLED(CONFIG_EFI_SOFT_RESERVE) &&
parse_option_str(val, "nosoftreserve");
if (parse_option_str(val, "disable_early_pci_dma"))
efi_disable_pci_dma = true;
if (parse_option_str(val, "no_disable_early_pci_dma"))
efi_disable_pci_dma = false;
}
} }
efi_bs_call(free_pool, buf);
/* return EFI_SUCCESS;
* We know source/dest won't overlap since both memory ranges
* have been allocated by UEFI, so we can safely use memcpy.
*/
memcpy((void *)new_addr, (void *)cur_image_addr, image_size);
/* Return the new address of the relocated image. */
*image_addr = new_addr;
return status;
} }
/* /*
@ -811,23 +160,19 @@ static u8 *efi_utf16_to_utf8(u8 *dst, const u16 *src, int n)
return dst; return dst;
} }
#ifndef MAX_CMDLINE_ADDRESS
#define MAX_CMDLINE_ADDRESS ULONG_MAX
#endif
/* /*
* Convert the unicode UEFI command line to ASCII to pass to kernel. * Convert the unicode UEFI command line to ASCII to pass to kernel.
* Size of memory allocated return in *cmd_line_len. * Size of memory allocated return in *cmd_line_len.
* Returns NULL on error. * Returns NULL on error.
*/ */
char *efi_convert_cmdline(efi_loaded_image_t *image, char *efi_convert_cmdline(efi_loaded_image_t *image,
int *cmd_line_len) int *cmd_line_len, unsigned long max_addr)
{ {
const u16 *s2; const u16 *s2;
u8 *s1 = NULL; u8 *s1 = NULL;
unsigned long cmdline_addr = 0; unsigned long cmdline_addr = 0;
int load_options_chars = image->load_options_size / 2; /* UTF-16 */ int load_options_chars = efi_table_attr(image, load_options_size) / 2;
const u16 *options = image->load_options; const u16 *options = efi_table_attr(image, load_options);
int options_bytes = 0; /* UTF-8 bytes */ int options_bytes = 0; /* UTF-8 bytes */
int options_chars = 0; /* UTF-16 chars */ int options_chars = 0; /* UTF-16 chars */
efi_status_t status; efi_status_t status;
@ -849,8 +194,7 @@ char *efi_convert_cmdline(efi_loaded_image_t *image,
options_bytes++; /* NUL termination */ options_bytes++; /* NUL termination */
status = efi_high_alloc(options_bytes, 0, &cmdline_addr, status = efi_allocate_pages(options_bytes, &cmdline_addr, max_addr);
MAX_CMDLINE_ADDRESS);
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
return NULL; return NULL;
@ -962,3 +306,89 @@ void efi_char16_printk(efi_char16_t *str)
efi_call_proto(efi_table_attr(efi_system_table(), con_out), efi_call_proto(efi_table_attr(efi_system_table(), con_out),
output_string, str); output_string, str);
} }
/*
* The LINUX_EFI_INITRD_MEDIA_GUID vendor media device path below provides a way
* for the firmware or bootloader to expose the initrd data directly to the stub
* via the trivial LoadFile2 protocol, which is defined in the UEFI spec, and is
* very easy to implement. It is a simple Linux initrd specific conduit between
* kernel and firmware, allowing us to put the EFI stub (being part of the
* kernel) in charge of where and when to load the initrd, while leaving it up
* to the firmware to decide whether it needs to expose its filesystem hierarchy
* via EFI protocols.
*/
static const struct {
struct efi_vendor_dev_path vendor;
struct efi_generic_dev_path end;
} __packed initrd_dev_path = {
{
{
EFI_DEV_MEDIA,
EFI_DEV_MEDIA_VENDOR,
sizeof(struct efi_vendor_dev_path),
},
LINUX_EFI_INITRD_MEDIA_GUID
}, {
EFI_DEV_END_PATH,
EFI_DEV_END_ENTIRE,
sizeof(struct efi_generic_dev_path)
}
};
/**
* efi_load_initrd_dev_path - load the initrd from the Linux initrd device path
* @load_addr: pointer to store the address where the initrd was loaded
* @load_size: pointer to store the size of the loaded initrd
* @max: upper limit for the initrd memory allocation
* @return: %EFI_SUCCESS if the initrd was loaded successfully, in which
* case @load_addr and @load_size are assigned accordingly
* %EFI_NOT_FOUND if no LoadFile2 protocol exists on the initrd
* device path
* %EFI_INVALID_PARAMETER if load_addr == NULL or load_size == NULL
* %EFI_OUT_OF_RESOURCES if memory allocation failed
* %EFI_LOAD_ERROR in all other cases
*/
efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr,
unsigned long *load_size,
unsigned long max)
{
efi_guid_t lf2_proto_guid = EFI_LOAD_FILE2_PROTOCOL_GUID;
efi_device_path_protocol_t *dp;
efi_load_file2_protocol_t *lf2;
unsigned long initrd_addr;
unsigned long initrd_size;
efi_handle_t handle;
efi_status_t status;
if (!load_addr || !load_size)
return EFI_INVALID_PARAMETER;
dp = (efi_device_path_protocol_t *)&initrd_dev_path;
status = efi_bs_call(locate_device_path, &lf2_proto_guid, &dp, &handle);
if (status != EFI_SUCCESS)
return status;
status = efi_bs_call(handle_protocol, handle, &lf2_proto_guid,
(void **)&lf2);
if (status != EFI_SUCCESS)
return status;
status = efi_call_proto(lf2, load_file, dp, false, &initrd_size, NULL);
if (status != EFI_BUFFER_TOO_SMALL)
return EFI_LOAD_ERROR;
status = efi_allocate_pages(initrd_size, &initrd_addr, max);
if (status != EFI_SUCCESS)
return status;
status = efi_call_proto(lf2, load_file, dp, false, &initrd_size,
(void *)initrd_addr);
if (status != EFI_SUCCESS) {
efi_free(initrd_size, initrd_addr);
return EFI_LOAD_ERROR;
}
*load_addr = initrd_addr;
*load_size = initrd_size;
return EFI_SUCCESS;
}

View File

@ -31,7 +31,9 @@
#define __efistub_global #define __efistub_global
#endif #endif
extern bool __pure nochunk(void);
extern bool __pure nokaslr(void); extern bool __pure nokaslr(void);
extern bool __pure noinitrd(void);
extern bool __pure is_quiet(void); extern bool __pure is_quiet(void);
extern bool __pure novamap(void); extern bool __pure novamap(void);
@ -43,10 +45,549 @@ extern __pure efi_system_table_t *efi_system_table(void);
#define pr_efi_err(msg) efi_printk("EFI stub: ERROR: "msg) #define pr_efi_err(msg) efi_printk("EFI stub: ERROR: "msg)
void efi_char16_printk(efi_char16_t *); /* Helper macros for the usual case of using simple C variables: */
void efi_char16_printk(efi_char16_t *); #ifndef fdt_setprop_inplace_var
#define fdt_setprop_inplace_var(fdt, node_offset, name, var) \
fdt_setprop_inplace((fdt), (node_offset), (name), &(var), sizeof(var))
#endif
unsigned long get_dram_base(void); #ifndef fdt_setprop_var
#define fdt_setprop_var(fdt, node_offset, name, var) \
fdt_setprop((fdt), (node_offset), (name), &(var), sizeof(var))
#endif
#define get_efi_var(name, vendor, ...) \
efi_rt_call(get_variable, (efi_char16_t *)(name), \
(efi_guid_t *)(vendor), __VA_ARGS__)
#define set_efi_var(name, vendor, ...) \
efi_rt_call(set_variable, (efi_char16_t *)(name), \
(efi_guid_t *)(vendor), __VA_ARGS__)
#define efi_get_handle_at(array, idx) \
(efi_is_native() ? (array)[idx] \
: (efi_handle_t)(unsigned long)((u32 *)(array))[idx])
#define efi_get_handle_num(size) \
((size) / (efi_is_native() ? sizeof(efi_handle_t) : sizeof(u32)))
#define for_each_efi_handle(handle, array, size, i) \
for (i = 0; \
i < efi_get_handle_num(size) && \
((handle = efi_get_handle_at((array), i)) || true); \
i++)
/*
* Allocation types for calls to boottime->allocate_pages.
*/
#define EFI_ALLOCATE_ANY_PAGES 0
#define EFI_ALLOCATE_MAX_ADDRESS 1
#define EFI_ALLOCATE_ADDRESS 2
#define EFI_MAX_ALLOCATE_TYPE 3
/*
* The type of search to perform when calling boottime->locate_handle
*/
#define EFI_LOCATE_ALL_HANDLES 0
#define EFI_LOCATE_BY_REGISTER_NOTIFY 1
#define EFI_LOCATE_BY_PROTOCOL 2
struct efi_boot_memmap {
efi_memory_desc_t **map;
unsigned long *map_size;
unsigned long *desc_size;
u32 *desc_ver;
unsigned long *key_ptr;
unsigned long *buff_size;
};
typedef struct efi_generic_dev_path efi_device_path_protocol_t;
/*
* EFI Boot Services table
*/
union efi_boot_services {
struct {
efi_table_hdr_t hdr;
void *raise_tpl;
void *restore_tpl;
efi_status_t (__efiapi *allocate_pages)(int, int, unsigned long,
efi_physical_addr_t *);
efi_status_t (__efiapi *free_pages)(efi_physical_addr_t,
unsigned long);
efi_status_t (__efiapi *get_memory_map)(unsigned long *, void *,
unsigned long *,
unsigned long *, u32 *);
efi_status_t (__efiapi *allocate_pool)(int, unsigned long,
void **);
efi_status_t (__efiapi *free_pool)(void *);
void *create_event;
void *set_timer;
void *wait_for_event;
void *signal_event;
void *close_event;
void *check_event;
void *install_protocol_interface;
void *reinstall_protocol_interface;
void *uninstall_protocol_interface;
efi_status_t (__efiapi *handle_protocol)(efi_handle_t,
efi_guid_t *, void **);
void *__reserved;
void *register_protocol_notify;
efi_status_t (__efiapi *locate_handle)(int, efi_guid_t *,
void *, unsigned long *,
efi_handle_t *);
efi_status_t (__efiapi *locate_device_path)(efi_guid_t *,
efi_device_path_protocol_t **,
efi_handle_t *);
efi_status_t (__efiapi *install_configuration_table)(efi_guid_t *,
void *);
void *load_image;
void *start_image;
efi_status_t __noreturn (__efiapi *exit)(efi_handle_t,
efi_status_t,
unsigned long,
efi_char16_t *);
void *unload_image;
efi_status_t (__efiapi *exit_boot_services)(efi_handle_t,
unsigned long);
void *get_next_monotonic_count;
void *stall;
void *set_watchdog_timer;
void *connect_controller;
efi_status_t (__efiapi *disconnect_controller)(efi_handle_t,
efi_handle_t,
efi_handle_t);
void *open_protocol;
void *close_protocol;
void *open_protocol_information;
void *protocols_per_handle;
void *locate_handle_buffer;
efi_status_t (__efiapi *locate_protocol)(efi_guid_t *, void *,
void **);
void *install_multiple_protocol_interfaces;
void *uninstall_multiple_protocol_interfaces;
void *calculate_crc32;
void *copy_mem;
void *set_mem;
void *create_event_ex;
};
struct {
efi_table_hdr_t hdr;
u32 raise_tpl;
u32 restore_tpl;
u32 allocate_pages;
u32 free_pages;
u32 get_memory_map;
u32 allocate_pool;
u32 free_pool;
u32 create_event;
u32 set_timer;
u32 wait_for_event;
u32 signal_event;
u32 close_event;
u32 check_event;
u32 install_protocol_interface;
u32 reinstall_protocol_interface;
u32 uninstall_protocol_interface;
u32 handle_protocol;
u32 __reserved;
u32 register_protocol_notify;
u32 locate_handle;
u32 locate_device_path;
u32 install_configuration_table;
u32 load_image;
u32 start_image;
u32 exit;
u32 unload_image;
u32 exit_boot_services;
u32 get_next_monotonic_count;
u32 stall;
u32 set_watchdog_timer;
u32 connect_controller;
u32 disconnect_controller;
u32 open_protocol;
u32 close_protocol;
u32 open_protocol_information;
u32 protocols_per_handle;
u32 locate_handle_buffer;
u32 locate_protocol;
u32 install_multiple_protocol_interfaces;
u32 uninstall_multiple_protocol_interfaces;
u32 calculate_crc32;
u32 copy_mem;
u32 set_mem;
u32 create_event_ex;
} mixed_mode;
};
typedef union efi_uga_draw_protocol efi_uga_draw_protocol_t;
union efi_uga_draw_protocol {
struct {
efi_status_t (__efiapi *get_mode)(efi_uga_draw_protocol_t *,
u32*, u32*, u32*, u32*);
void *set_mode;
void *blt;
};
struct {
u32 get_mode;
u32 set_mode;
u32 blt;
} mixed_mode;
};
union efi_simple_text_output_protocol {
struct {
void *reset;
efi_status_t (__efiapi *output_string)(efi_simple_text_output_protocol_t *,
efi_char16_t *);
void *test_string;
};
struct {
u32 reset;
u32 output_string;
u32 test_string;
} mixed_mode;
};
#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
typedef struct {
u32 red_mask;
u32 green_mask;
u32 blue_mask;
u32 reserved_mask;
} efi_pixel_bitmask_t;
typedef struct {
u32 version;
u32 horizontal_resolution;
u32 vertical_resolution;
int pixel_format;
efi_pixel_bitmask_t pixel_information;
u32 pixels_per_scan_line;
} efi_graphics_output_mode_info_t;
typedef union efi_graphics_output_protocol_mode efi_graphics_output_protocol_mode_t;
union efi_graphics_output_protocol_mode {
struct {
u32 max_mode;
u32 mode;
efi_graphics_output_mode_info_t *info;
unsigned long size_of_info;
efi_physical_addr_t frame_buffer_base;
unsigned long frame_buffer_size;
};
struct {
u32 max_mode;
u32 mode;
u32 info;
u32 size_of_info;
u64 frame_buffer_base;
u32 frame_buffer_size;
} mixed_mode;
};
typedef union efi_graphics_output_protocol efi_graphics_output_protocol_t;
union efi_graphics_output_protocol {
struct {
void *query_mode;
void *set_mode;
void *blt;
efi_graphics_output_protocol_mode_t *mode;
};
struct {
u32 query_mode;
u32 set_mode;
u32 blt;
u32 mode;
} mixed_mode;
};
typedef union {
struct {
u32 revision;
efi_handle_t parent_handle;
efi_system_table_t *system_table;
efi_handle_t device_handle;
void *file_path;
void *reserved;
u32 load_options_size;
void *load_options;
void *image_base;
__aligned_u64 image_size;
unsigned int image_code_type;
unsigned int image_data_type;
efi_status_t (__efiapi *unload)(efi_handle_t image_handle);
};
struct {
u32 revision;
u32 parent_handle;
u32 system_table;
u32 device_handle;
u32 file_path;
u32 reserved;
u32 load_options_size;
u32 load_options;
u32 image_base;
__aligned_u64 image_size;
u32 image_code_type;
u32 image_data_type;
u32 unload;
} mixed_mode;
} efi_loaded_image_t;
typedef struct {
u64 size;
u64 file_size;
u64 phys_size;
efi_time_t create_time;
efi_time_t last_access_time;
efi_time_t modification_time;
__aligned_u64 attribute;
efi_char16_t filename[];
} efi_file_info_t;
typedef struct efi_file_protocol efi_file_protocol_t;
struct efi_file_protocol {
u64 revision;
efi_status_t (__efiapi *open) (efi_file_protocol_t *,
efi_file_protocol_t **,
efi_char16_t *, u64, u64);
efi_status_t (__efiapi *close) (efi_file_protocol_t *);
efi_status_t (__efiapi *delete) (efi_file_protocol_t *);
efi_status_t (__efiapi *read) (efi_file_protocol_t *,
unsigned long *, void *);
efi_status_t (__efiapi *write) (efi_file_protocol_t *,
unsigned long, void *);
efi_status_t (__efiapi *get_position)(efi_file_protocol_t *, u64 *);
efi_status_t (__efiapi *set_position)(efi_file_protocol_t *, u64);
efi_status_t (__efiapi *get_info) (efi_file_protocol_t *,
efi_guid_t *, unsigned long *,
void *);
efi_status_t (__efiapi *set_info) (efi_file_protocol_t *,
efi_guid_t *, unsigned long,
void *);
efi_status_t (__efiapi *flush) (efi_file_protocol_t *);
};
typedef struct efi_simple_file_system_protocol efi_simple_file_system_protocol_t;
struct efi_simple_file_system_protocol {
u64 revision;
int (__efiapi *open_volume)(efi_simple_file_system_protocol_t *,
efi_file_protocol_t **);
};
#define EFI_FILE_MODE_READ 0x0000000000000001
#define EFI_FILE_MODE_WRITE 0x0000000000000002
#define EFI_FILE_MODE_CREATE 0x8000000000000000
typedef enum {
EfiPciIoWidthUint8,
EfiPciIoWidthUint16,
EfiPciIoWidthUint32,
EfiPciIoWidthUint64,
EfiPciIoWidthFifoUint8,
EfiPciIoWidthFifoUint16,
EfiPciIoWidthFifoUint32,
EfiPciIoWidthFifoUint64,
EfiPciIoWidthFillUint8,
EfiPciIoWidthFillUint16,
EfiPciIoWidthFillUint32,
EfiPciIoWidthFillUint64,
EfiPciIoWidthMaximum
} EFI_PCI_IO_PROTOCOL_WIDTH;
typedef enum {
EfiPciIoAttributeOperationGet,
EfiPciIoAttributeOperationSet,
EfiPciIoAttributeOperationEnable,
EfiPciIoAttributeOperationDisable,
EfiPciIoAttributeOperationSupported,
EfiPciIoAttributeOperationMaximum
} EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION;
typedef struct {
u32 read;
u32 write;
} efi_pci_io_protocol_access_32_t;
typedef union efi_pci_io_protocol efi_pci_io_protocol_t;
typedef
efi_status_t (__efiapi *efi_pci_io_protocol_cfg_t)(efi_pci_io_protocol_t *,
EFI_PCI_IO_PROTOCOL_WIDTH,
u32 offset,
unsigned long count,
void *buffer);
typedef struct {
void *read;
void *write;
} efi_pci_io_protocol_access_t;
typedef struct {
efi_pci_io_protocol_cfg_t read;
efi_pci_io_protocol_cfg_t write;
} efi_pci_io_protocol_config_access_t;
union efi_pci_io_protocol {
struct {
void *poll_mem;
void *poll_io;
efi_pci_io_protocol_access_t mem;
efi_pci_io_protocol_access_t io;
efi_pci_io_protocol_config_access_t pci;
void *copy_mem;
void *map;
void *unmap;
void *allocate_buffer;
void *free_buffer;
void *flush;
efi_status_t (__efiapi *get_location)(efi_pci_io_protocol_t *,
unsigned long *segment_nr,
unsigned long *bus_nr,
unsigned long *device_nr,
unsigned long *func_nr);
void *attributes;
void *get_bar_attributes;
void *set_bar_attributes;
uint64_t romsize;
void *romimage;
};
struct {
u32 poll_mem;
u32 poll_io;
efi_pci_io_protocol_access_32_t mem;
efi_pci_io_protocol_access_32_t io;
efi_pci_io_protocol_access_32_t pci;
u32 copy_mem;
u32 map;
u32 unmap;
u32 allocate_buffer;
u32 free_buffer;
u32 flush;
u32 get_location;
u32 attributes;
u32 get_bar_attributes;
u32 set_bar_attributes;
u64 romsize;
u32 romimage;
} mixed_mode;
};
#define EFI_PCI_IO_ATTRIBUTE_ISA_MOTHERBOARD_IO 0x0001
#define EFI_PCI_IO_ATTRIBUTE_ISA_IO 0x0002
#define EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO 0x0004
#define EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY 0x0008
#define EFI_PCI_IO_ATTRIBUTE_VGA_IO 0x0010
#define EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO 0x0020
#define EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO 0x0040
#define EFI_PCI_IO_ATTRIBUTE_MEMORY_WRITE_COMBINE 0x0080
#define EFI_PCI_IO_ATTRIBUTE_IO 0x0100
#define EFI_PCI_IO_ATTRIBUTE_MEMORY 0x0200
#define EFI_PCI_IO_ATTRIBUTE_BUS_MASTER 0x0400
#define EFI_PCI_IO_ATTRIBUTE_MEMORY_CACHED 0x0800
#define EFI_PCI_IO_ATTRIBUTE_MEMORY_DISABLE 0x1000
#define EFI_PCI_IO_ATTRIBUTE_EMBEDDED_DEVICE 0x2000
#define EFI_PCI_IO_ATTRIBUTE_EMBEDDED_ROM 0x4000
#define EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE 0x8000
#define EFI_PCI_IO_ATTRIBUTE_ISA_IO_16 0x10000
#define EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO_16 0x20000
#define EFI_PCI_IO_ATTRIBUTE_VGA_IO_16 0x40000
struct efi_dev_path;
typedef union apple_properties_protocol apple_properties_protocol_t;
union apple_properties_protocol {
struct {
unsigned long version;
efi_status_t (__efiapi *get)(apple_properties_protocol_t *,
struct efi_dev_path *,
efi_char16_t *, void *, u32 *);
efi_status_t (__efiapi *set)(apple_properties_protocol_t *,
struct efi_dev_path *,
efi_char16_t *, void *, u32);
efi_status_t (__efiapi *del)(apple_properties_protocol_t *,
struct efi_dev_path *,
efi_char16_t *);
efi_status_t (__efiapi *get_all)(apple_properties_protocol_t *,
void *buffer, u32 *);
};
struct {
u32 version;
u32 get;
u32 set;
u32 del;
u32 get_all;
} mixed_mode;
};
typedef u32 efi_tcg2_event_log_format;
typedef union efi_tcg2_protocol efi_tcg2_protocol_t;
union efi_tcg2_protocol {
struct {
void *get_capability;
efi_status_t (__efiapi *get_event_log)(efi_handle_t,
efi_tcg2_event_log_format,
efi_physical_addr_t *,
efi_physical_addr_t *,
efi_bool_t *);
void *hash_log_extend_event;
void *submit_command;
void *get_active_pcr_banks;
void *set_active_pcr_banks;
void *get_result_of_set_active_pcr_banks;
};
struct {
u32 get_capability;
u32 get_event_log;
u32 hash_log_extend_event;
u32 submit_command;
u32 get_active_pcr_banks;
u32 set_active_pcr_banks;
u32 get_result_of_set_active_pcr_banks;
} mixed_mode;
};
typedef union efi_load_file_protocol efi_load_file_protocol_t;
typedef union efi_load_file_protocol efi_load_file2_protocol_t;
union efi_load_file_protocol {
struct {
efi_status_t (__efiapi *load_file)(efi_load_file_protocol_t *,
efi_device_path_protocol_t *,
bool, unsigned long *, void *);
};
struct {
u32 load_file;
} mixed_mode;
};
void efi_pci_disable_bridge_busmaster(void);
typedef efi_status_t (*efi_exit_boot_map_processing)(
struct efi_boot_memmap *map,
void *priv);
efi_status_t efi_exit_boot_services(void *handle,
struct efi_boot_memmap *map,
void *priv,
efi_exit_boot_map_processing priv_func);
void efi_char16_printk(efi_char16_t *);
efi_status_t allocate_new_fdt_and_exit_boot(void *handle, efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
unsigned long *new_fdt_addr, unsigned long *new_fdt_addr,
@ -71,23 +612,57 @@ efi_status_t check_platform_features(void);
void *get_efi_config_table(efi_guid_t guid); void *get_efi_config_table(efi_guid_t guid);
/* Helper macros for the usual case of using simple C variables: */ void efi_printk(char *str);
#ifndef fdt_setprop_inplace_var
#define fdt_setprop_inplace_var(fdt, node_offset, name, var) \
fdt_setprop_inplace((fdt), (node_offset), (name), &(var), sizeof(var))
#endif
#ifndef fdt_setprop_var void efi_free(unsigned long size, unsigned long addr);
#define fdt_setprop_var(fdt, node_offset, name, var) \
fdt_setprop((fdt), (node_offset), (name), &(var), sizeof(var))
#endif
#define get_efi_var(name, vendor, ...) \ char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len,
efi_rt_call(get_variable, (efi_char16_t *)(name), \ unsigned long max_addr);
(efi_guid_t *)(vendor), __VA_ARGS__)
#define set_efi_var(name, vendor, ...) \ efi_status_t efi_get_memory_map(struct efi_boot_memmap *map);
efi_rt_call(set_variable, (efi_char16_t *)(name), \
(efi_guid_t *)(vendor), __VA_ARGS__) efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align,
unsigned long *addr, unsigned long min);
static inline
efi_status_t efi_low_alloc(unsigned long size, unsigned long align,
unsigned long *addr)
{
/*
* Don't allocate at 0x0. It will confuse code that
* checks pointers against NULL. Skip the first 8
* bytes so we start at a nice even number.
*/
return efi_low_alloc_above(size, align, addr, 0x8);
}
efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr,
unsigned long max);
efi_status_t efi_relocate_kernel(unsigned long *image_addr,
unsigned long image_size,
unsigned long alloc_size,
unsigned long preferred_addr,
unsigned long alignment,
unsigned long min_addr);
efi_status_t efi_parse_options(char const *cmdline);
efi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto,
unsigned long size);
efi_status_t efi_load_dtb(efi_loaded_image_t *image,
unsigned long *load_addr,
unsigned long *load_size);
efi_status_t efi_load_initrd(efi_loaded_image_t *image,
unsigned long *load_addr,
unsigned long *load_size,
unsigned long soft_limit,
unsigned long hard_limit);
efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr,
unsigned long *load_size,
unsigned long max);
#endif #endif

View File

@ -199,10 +199,6 @@ static efi_status_t update_fdt_memmap(void *fdt, struct efi_boot_memmap *map)
return EFI_SUCCESS; return EFI_SUCCESS;
} }
#ifndef EFI_FDT_ALIGN
# define EFI_FDT_ALIGN EFI_PAGE_SIZE
#endif
struct exit_boot_struct { struct exit_boot_struct {
efi_memory_desc_t *runtime_map; efi_memory_desc_t *runtime_map;
int *runtime_entry_count; int *runtime_entry_count;
@ -281,8 +277,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
pr_efi("Exiting boot services and installing virtual address map...\n"); pr_efi("Exiting boot services and installing virtual address map...\n");
map.map = &memory_map; map.map = &memory_map;
status = efi_high_alloc(MAX_FDT_SIZE, EFI_FDT_ALIGN, status = efi_allocate_pages(MAX_FDT_SIZE, new_fdt_addr, max_addr);
new_fdt_addr, max_addr);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
pr_efi_err("Unable to allocate memory for new device tree.\n"); pr_efi_err("Unable to allocate memory for new device tree.\n");
goto fail; goto fail;

View File

@ -0,0 +1,258 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Helper functions used by the EFI stub on multiple
* architectures. This should be #included by the EFI stub
* implementation files.
*
* Copyright 2011 Intel Corporation; author Matt Fleming
*/
#include <linux/efi.h>
#include <asm/efi.h>
#include "efistub.h"
#define MAX_FILENAME_SIZE 256
/*
* Some firmware implementations have problems reading files in one go.
* A read chunk size of 1MB seems to work for most platforms.
*
* Unfortunately, reading files in chunks triggers *other* bugs on some
* platforms, so we provide a way to disable this workaround, which can
* be done by passing "efi=nochunk" on the EFI boot stub command line.
*
* If you experience issues with initrd images being corrupt it's worth
* trying efi=nochunk, but chunking is enabled by default on x86 because
* there are far more machines that require the workaround than those that
* break with it enabled.
*/
#define EFI_READ_CHUNK_SIZE SZ_1M
static efi_status_t efi_open_file(efi_file_protocol_t *volume,
efi_char16_t *filename_16,
efi_file_protocol_t **handle,
unsigned long *file_size)
{
struct {
efi_file_info_t info;
efi_char16_t filename[MAX_FILENAME_SIZE];
} finfo;
efi_guid_t info_guid = EFI_FILE_INFO_ID;
efi_file_protocol_t *fh;
unsigned long info_sz;
efi_status_t status;
status = volume->open(volume, &fh, filename_16, EFI_FILE_MODE_READ, 0);
if (status != EFI_SUCCESS) {
pr_efi_err("Failed to open file: ");
efi_char16_printk(filename_16);
efi_printk("\n");
return status;
}
info_sz = sizeof(finfo);
status = fh->get_info(fh, &info_guid, &info_sz, &finfo);
if (status != EFI_SUCCESS) {
pr_efi_err("Failed to get file info\n");
fh->close(fh);
return status;
}
*handle = fh;
*file_size = finfo.info.file_size;
return EFI_SUCCESS;
}
static efi_status_t efi_open_volume(efi_loaded_image_t *image,
efi_file_protocol_t **fh)
{
efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID;
efi_simple_file_system_protocol_t *io;
efi_status_t status;
status = efi_bs_call(handle_protocol, image->device_handle, &fs_proto,
(void **)&io);
if (status != EFI_SUCCESS) {
pr_efi_err("Failed to handle fs_proto\n");
return status;
}
status = io->open_volume(io, fh);
if (status != EFI_SUCCESS)
pr_efi_err("Failed to open volume\n");
return status;
}
static int find_file_option(const efi_char16_t *cmdline, int cmdline_len,
const efi_char16_t *prefix, int prefix_size,
efi_char16_t *result, int result_len)
{
int prefix_len = prefix_size / 2;
bool found = false;
int i;
for (i = prefix_len; i < cmdline_len; i++) {
if (!memcmp(&cmdline[i - prefix_len], prefix, prefix_size)) {
found = true;
break;
}
}
if (!found)
return 0;
while (--result_len > 0 && i < cmdline_len) {
if (cmdline[i] == L'\0' ||
cmdline[i] == L'\n' ||
cmdline[i] == L' ')
break;
*result++ = cmdline[i++];
}
*result = L'\0';
return i;
}
/*
* Check the cmdline for a LILO-style file= arguments.
*
* We only support loading a file from the same filesystem as
* the kernel image.
*/
static efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
const efi_char16_t *optstr,
int optstr_size,
unsigned long soft_limit,
unsigned long hard_limit,
unsigned long *load_addr,
unsigned long *load_size)
{
const efi_char16_t *cmdline = image->load_options;
int cmdline_len = image->load_options_size / 2;
unsigned long efi_chunk_size = ULONG_MAX;
efi_file_protocol_t *volume = NULL;
efi_file_protocol_t *file;
unsigned long alloc_addr;
unsigned long alloc_size;
efi_status_t status;
int offset;
if (!load_addr || !load_size)
return EFI_INVALID_PARAMETER;
if (IS_ENABLED(CONFIG_X86) && !nochunk())
efi_chunk_size = EFI_READ_CHUNK_SIZE;
alloc_addr = alloc_size = 0;
do {
efi_char16_t filename[MAX_FILENAME_SIZE];
unsigned long size;
void *addr;
offset = find_file_option(cmdline, cmdline_len,
optstr, optstr_size,
filename, ARRAY_SIZE(filename));
if (!offset)
break;
cmdline += offset;
cmdline_len -= offset;
if (!volume) {
status = efi_open_volume(image, &volume);
if (status != EFI_SUCCESS)
return status;
}
status = efi_open_file(volume, filename, &file, &size);
if (status != EFI_SUCCESS)
goto err_close_volume;
/*
* Check whether the existing allocation can contain the next
* file. This condition will also trigger naturally during the
* first (and typically only) iteration of the loop, given that
* alloc_size == 0 in that case.
*/
if (round_up(alloc_size + size, EFI_ALLOC_ALIGN) >
round_up(alloc_size, EFI_ALLOC_ALIGN)) {
unsigned long old_addr = alloc_addr;
status = EFI_OUT_OF_RESOURCES;
if (soft_limit < hard_limit)
status = efi_allocate_pages(alloc_size + size,
&alloc_addr,
soft_limit);
if (status == EFI_OUT_OF_RESOURCES)
status = efi_allocate_pages(alloc_size + size,
&alloc_addr,
hard_limit);
if (status != EFI_SUCCESS) {
pr_efi_err("Failed to allocate memory for files\n");
goto err_close_file;
}
if (old_addr != 0) {
/*
* This is not the first time we've gone
* around this loop, and so we are loading
* multiple files that need to be concatenated
* and returned in a single buffer.
*/
memcpy((void *)alloc_addr, (void *)old_addr, alloc_size);
efi_free(alloc_size, old_addr);
}
}
addr = (void *)alloc_addr + alloc_size;
alloc_size += size;
while (size) {
unsigned long chunksize = min(size, efi_chunk_size);
status = file->read(file, &chunksize, addr);
if (status != EFI_SUCCESS) {
pr_efi_err("Failed to read file\n");
goto err_close_file;
}
addr += chunksize;
size -= chunksize;
}
file->close(file);
} while (offset > 0);
*load_addr = alloc_addr;
*load_size = alloc_size;
if (volume)
volume->close(volume);
return EFI_SUCCESS;
err_close_file:
file->close(file);
err_close_volume:
volume->close(volume);
efi_free(alloc_size, alloc_addr);
return status;
}
efi_status_t efi_load_dtb(efi_loaded_image_t *image,
unsigned long *load_addr,
unsigned long *load_size)
{
return handle_cmdline_files(image, L"dtb=", sizeof(L"dtb=") - 2,
ULONG_MAX, ULONG_MAX, load_addr, load_size);
}
efi_status_t efi_load_initrd(efi_loaded_image_t *image,
unsigned long *load_addr,
unsigned long *load_size,
unsigned long soft_limit,
unsigned long hard_limit)
{
return handle_cmdline_files(image, L"initrd=", sizeof(L"initrd=") - 2,
soft_limit, hard_limit, load_addr, load_size);
}

View File

@ -0,0 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* To prevent the compiler from emitting GOT-indirected (and thus absolute)
* references to any global symbols, override their visibility as 'hidden'
*/
#pragma GCC visibility push(hidden)

View File

@ -0,0 +1,309 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/efi.h>
#include <asm/efi.h>
#include "efistub.h"
#define EFI_MMAP_NR_SLACK_SLOTS 8
static inline bool mmap_has_headroom(unsigned long buff_size,
unsigned long map_size,
unsigned long desc_size)
{
unsigned long slack = buff_size - map_size;
return slack / desc_size >= EFI_MMAP_NR_SLACK_SLOTS;
}
/**
* efi_get_memory_map() - get memory map
* @map: on return pointer to memory map
*
* Retrieve the UEFI memory map. The allocated memory leaves room for
* up to EFI_MMAP_NR_SLACK_SLOTS additional memory map entries.
*
* Return: status code
*/
efi_status_t efi_get_memory_map(struct efi_boot_memmap *map)
{
efi_memory_desc_t *m = NULL;
efi_status_t status;
unsigned long key;
u32 desc_version;
*map->desc_size = sizeof(*m);
*map->map_size = *map->desc_size * 32;
*map->buff_size = *map->map_size;
again:
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
*map->map_size, (void **)&m);
if (status != EFI_SUCCESS)
goto fail;
*map->desc_size = 0;
key = 0;
status = efi_bs_call(get_memory_map, map->map_size, m,
&key, map->desc_size, &desc_version);
if (status == EFI_BUFFER_TOO_SMALL ||
!mmap_has_headroom(*map->buff_size, *map->map_size,
*map->desc_size)) {
efi_bs_call(free_pool, m);
/*
* Make sure there is some entries of headroom so that the
* buffer can be reused for a new map after allocations are
* no longer permitted. Its unlikely that the map will grow to
* exceed this headroom once we are ready to trigger
* ExitBootServices()
*/
*map->map_size += *map->desc_size * EFI_MMAP_NR_SLACK_SLOTS;
*map->buff_size = *map->map_size;
goto again;
}
if (status == EFI_SUCCESS) {
if (map->key_ptr)
*map->key_ptr = key;
if (map->desc_ver)
*map->desc_ver = desc_version;
} else {
efi_bs_call(free_pool, m);
}
fail:
*map->map = m;
return status;
}
/**
* efi_allocate_pages() - Allocate memory pages
* @size: minimum number of bytes to allocate
* @addr: On return the address of the first allocated page. The first
* allocated page has alignment EFI_ALLOC_ALIGN which is an
* architecture dependent multiple of the page size.
* @max: the address that the last allocated memory page shall not
* exceed
*
* Allocate pages as EFI_LOADER_DATA. The allocated pages are aligned according
* to EFI_ALLOC_ALIGN. The last allocated page will not exceed the address
* given by @max.
*
* Return: status code
*/
efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr,
unsigned long max)
{
efi_physical_addr_t alloc_addr = ALIGN_DOWN(max + 1, EFI_ALLOC_ALIGN) - 1;
int slack = EFI_ALLOC_ALIGN / EFI_PAGE_SIZE - 1;
efi_status_t status;
size = round_up(size, EFI_ALLOC_ALIGN);
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS,
EFI_LOADER_DATA, size / EFI_PAGE_SIZE + slack,
&alloc_addr);
if (status != EFI_SUCCESS)
return status;
*addr = ALIGN((unsigned long)alloc_addr, EFI_ALLOC_ALIGN);
if (slack > 0) {
int l = (alloc_addr % EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
if (l) {
efi_bs_call(free_pages, alloc_addr, slack - l + 1);
slack = l - 1;
}
if (slack)
efi_bs_call(free_pages, *addr + size, slack);
}
return EFI_SUCCESS;
}
/**
* efi_low_alloc_above() - allocate pages at or above given address
* @size: size of the memory area to allocate
* @align: minimum alignment of the allocated memory area. It should
* a power of two.
* @addr: on exit the address of the allocated memory
* @min: minimum address to used for the memory allocation
*
* Allocate at the lowest possible address that is not below @min as
* EFI_LOADER_DATA. The allocated pages are aligned according to @align but at
* least EFI_ALLOC_ALIGN. The first allocated page will not below the address
* given by @min.
*
* Return: status code
*/
efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align,
unsigned long *addr, unsigned long min)
{
unsigned long map_size, desc_size, buff_size;
efi_memory_desc_t *map;
efi_status_t status;
unsigned long nr_pages;
int i;
struct efi_boot_memmap boot_map;
boot_map.map = &map;
boot_map.map_size = &map_size;
boot_map.desc_size = &desc_size;
boot_map.desc_ver = NULL;
boot_map.key_ptr = NULL;
boot_map.buff_size = &buff_size;
status = efi_get_memory_map(&boot_map);
if (status != EFI_SUCCESS)
goto fail;
/*
* Enforce minimum alignment that EFI or Linux requires when
* requesting a specific address. We are doing page-based (or
* larger) allocations, and both the address and size must meet
* alignment constraints.
*/
if (align < EFI_ALLOC_ALIGN)
align = EFI_ALLOC_ALIGN;
size = round_up(size, EFI_ALLOC_ALIGN);
nr_pages = size / EFI_PAGE_SIZE;
for (i = 0; i < map_size / desc_size; i++) {
efi_memory_desc_t *desc;
unsigned long m = (unsigned long)map;
u64 start, end;
desc = efi_early_memdesc_ptr(m, desc_size, i);
if (desc->type != EFI_CONVENTIONAL_MEMORY)
continue;
if (efi_soft_reserve_enabled() &&
(desc->attribute & EFI_MEMORY_SP))
continue;
if (desc->num_pages < nr_pages)
continue;
start = desc->phys_addr;
end = start + desc->num_pages * EFI_PAGE_SIZE;
if (start < min)
start = min;
start = round_up(start, align);
if ((start + size) > end)
continue;
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
EFI_LOADER_DATA, nr_pages, &start);
if (status == EFI_SUCCESS) {
*addr = start;
break;
}
}
if (i == map_size / desc_size)
status = EFI_NOT_FOUND;
efi_bs_call(free_pool, map);
fail:
return status;
}
/**
* efi_free() - free memory pages
* @size: size of the memory area to free in bytes
* @addr: start of the memory area to free (must be EFI_PAGE_SIZE
* aligned)
*
* @size is rounded up to a multiple of EFI_ALLOC_ALIGN which is an
* architecture specific multiple of EFI_PAGE_SIZE. So this function should
* only be used to return pages allocated with efi_allocate_pages() or
* efi_low_alloc_above().
*/
void efi_free(unsigned long size, unsigned long addr)
{
unsigned long nr_pages;
if (!size)
return;
nr_pages = round_up(size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
efi_bs_call(free_pages, addr, nr_pages);
}
/**
* efi_relocate_kernel() - copy memory area
* @image_addr: pointer to address of memory area to copy
* @image_size: size of memory area to copy
* @alloc_size: minimum size of memory to allocate, must be greater or
* equal to image_size
* @preferred_addr: preferred target address
* @alignment: minimum alignment of the allocated memory area. It
* should be a power of two.
* @min_addr: minimum target address
*
* Copy a memory area to a newly allocated memory area aligned according
* to @alignment but at least EFI_ALLOC_ALIGN. If the preferred address
* is not available, the allocated address will not be below @min_addr.
* On exit, @image_addr is updated to the target copy address that was used.
*
* This function is used to copy the Linux kernel verbatim. It does not apply
* any relocation changes.
*
* Return: status code
*/
efi_status_t efi_relocate_kernel(unsigned long *image_addr,
unsigned long image_size,
unsigned long alloc_size,
unsigned long preferred_addr,
unsigned long alignment,
unsigned long min_addr)
{
unsigned long cur_image_addr;
unsigned long new_addr = 0;
efi_status_t status;
unsigned long nr_pages;
efi_physical_addr_t efi_addr = preferred_addr;
if (!image_addr || !image_size || !alloc_size)
return EFI_INVALID_PARAMETER;
if (alloc_size < image_size)
return EFI_INVALID_PARAMETER;
cur_image_addr = *image_addr;
/*
* The EFI firmware loader could have placed the kernel image
* anywhere in memory, but the kernel has restrictions on the
* max physical address it can run at. Some architectures
* also have a prefered address, so first try to relocate
* to the preferred address. If that fails, allocate as low
* as possible while respecting the required alignment.
*/
nr_pages = round_up(alloc_size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
EFI_LOADER_DATA, nr_pages, &efi_addr);
new_addr = efi_addr;
/*
* If preferred address allocation failed allocate as low as
* possible.
*/
if (status != EFI_SUCCESS) {
status = efi_low_alloc_above(alloc_size, alignment, &new_addr,
min_addr);
}
if (status != EFI_SUCCESS) {
pr_efi_err("Failed to allocate usable memory for kernel.\n");
return status;
}
/*
* We know source/dest won't overlap since both memory ranges
* have been allocated by UEFI, so we can safely use memcpy.
*/
memcpy((void *)new_addr, (void *)cur_image_addr, image_size);
/* Return the new address of the relocated image. */
*image_addr = new_addr;
return status;
}

View File

@ -4,7 +4,6 @@
*/ */
#include <linux/efi.h> #include <linux/efi.h>
#include <linux/log2.h>
#include <asm/efi.h> #include <asm/efi.h>
#include "efistub.h" #include "efistub.h"
@ -26,6 +25,17 @@ union efi_rng_protocol {
} mixed_mode; } mixed_mode;
}; };
/**
* efi_get_random_bytes() - fill a buffer with random bytes
* @size: size of the buffer
* @out: caller allocated buffer to receive the random bytes
*
* The call will fail if either the firmware does not implement the
* EFI_RNG_PROTOCOL or there are not enough random bytes available to fill
* the buffer.
*
* Return: status code
*/
efi_status_t efi_get_random_bytes(unsigned long size, u8 *out) efi_status_t efi_get_random_bytes(unsigned long size, u8 *out)
{ {
efi_guid_t rng_proto = EFI_RNG_PROTOCOL_GUID; efi_guid_t rng_proto = EFI_RNG_PROTOCOL_GUID;
@ -39,119 +49,19 @@ efi_status_t efi_get_random_bytes(unsigned long size, u8 *out)
return efi_call_proto(rng, get_rng, NULL, size, out); return efi_call_proto(rng, get_rng, NULL, size, out);
} }
/* /**
* Return the number of slots covered by this entry, i.e., the number of * efi_random_get_seed() - provide random seed as configuration table
* addresses it covers that are suitably aligned and supply enough room *
* for the allocation. * The EFI_RNG_PROTOCOL is used to read random bytes. These random bytes are
* saved as a configuration table which can be used as entropy by the kernel
* for the initialization of its pseudo random number generator.
*
* If the EFI_RNG_PROTOCOL is not available or there are not enough random bytes
* available, the configuration table will not be installed and an error code
* will be returned.
*
* Return: status code
*/ */
static unsigned long get_entry_num_slots(efi_memory_desc_t *md,
unsigned long size,
unsigned long align_shift)
{
unsigned long align = 1UL << align_shift;
u64 first_slot, last_slot, region_end;
if (md->type != EFI_CONVENTIONAL_MEMORY)
return 0;
if (efi_soft_reserve_enabled() &&
(md->attribute & EFI_MEMORY_SP))
return 0;
region_end = min((u64)ULONG_MAX, md->phys_addr + md->num_pages*EFI_PAGE_SIZE - 1);
first_slot = round_up(md->phys_addr, align);
last_slot = round_down(region_end - size + 1, align);
if (first_slot > last_slot)
return 0;
return ((unsigned long)(last_slot - first_slot) >> align_shift) + 1;
}
/*
* The UEFI memory descriptors have a virtual address field that is only used
* when installing the virtual mapping using SetVirtualAddressMap(). Since it
* is unused here, we can reuse it to keep track of each descriptor's slot
* count.
*/
#define MD_NUM_SLOTS(md) ((md)->virt_addr)
efi_status_t efi_random_alloc(unsigned long size,
unsigned long align,
unsigned long *addr,
unsigned long random_seed)
{
unsigned long map_size, desc_size, total_slots = 0, target_slot;
unsigned long buff_size;
efi_status_t status;
efi_memory_desc_t *memory_map;
int map_offset;
struct efi_boot_memmap map;
map.map = &memory_map;
map.map_size = &map_size;
map.desc_size = &desc_size;
map.desc_ver = NULL;
map.key_ptr = NULL;
map.buff_size = &buff_size;
status = efi_get_memory_map(&map);
if (status != EFI_SUCCESS)
return status;
if (align < EFI_ALLOC_ALIGN)
align = EFI_ALLOC_ALIGN;
/* count the suitable slots in each memory map entry */
for (map_offset = 0; map_offset < map_size; map_offset += desc_size) {
efi_memory_desc_t *md = (void *)memory_map + map_offset;
unsigned long slots;
slots = get_entry_num_slots(md, size, ilog2(align));
MD_NUM_SLOTS(md) = slots;
total_slots += slots;
}
/* find a random number between 0 and total_slots */
target_slot = (total_slots * (u16)random_seed) >> 16;
/*
* target_slot is now a value in the range [0, total_slots), and so
* it corresponds with exactly one of the suitable slots we recorded
* when iterating over the memory map the first time around.
*
* So iterate over the memory map again, subtracting the number of
* slots of each entry at each iteration, until we have found the entry
* that covers our chosen slot. Use the residual value of target_slot
* to calculate the randomly chosen address, and allocate it directly
* using EFI_ALLOCATE_ADDRESS.
*/
for (map_offset = 0; map_offset < map_size; map_offset += desc_size) {
efi_memory_desc_t *md = (void *)memory_map + map_offset;
efi_physical_addr_t target;
unsigned long pages;
if (target_slot >= MD_NUM_SLOTS(md)) {
target_slot -= MD_NUM_SLOTS(md);
continue;
}
target = round_up(md->phys_addr, align) + target_slot * align;
pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
EFI_LOADER_DATA, pages, &target);
if (status == EFI_SUCCESS)
*addr = target;
break;
}
efi_bs_call(free_pool, memory_map);
return status;
}
efi_status_t efi_random_get_seed(void) efi_status_t efi_random_get_seed(void)
{ {
efi_guid_t rng_proto = EFI_RNG_PROTOCOL_GUID; efi_guid_t rng_proto = EFI_RNG_PROTOCOL_GUID;

View File

@ -0,0 +1,124 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2016 Linaro Ltd; <ard.biesheuvel@linaro.org>
*/
#include <linux/efi.h>
#include <linux/log2.h>
#include <asm/efi.h>
#include "efistub.h"
/*
* Return the number of slots covered by this entry, i.e., the number of
* addresses it covers that are suitably aligned and supply enough room
* for the allocation.
*/
static unsigned long get_entry_num_slots(efi_memory_desc_t *md,
unsigned long size,
unsigned long align_shift)
{
unsigned long align = 1UL << align_shift;
u64 first_slot, last_slot, region_end;
if (md->type != EFI_CONVENTIONAL_MEMORY)
return 0;
if (efi_soft_reserve_enabled() &&
(md->attribute & EFI_MEMORY_SP))
return 0;
region_end = min(md->phys_addr + md->num_pages * EFI_PAGE_SIZE - 1,
(u64)ULONG_MAX);
first_slot = round_up(md->phys_addr, align);
last_slot = round_down(region_end - size + 1, align);
if (first_slot > last_slot)
return 0;
return ((unsigned long)(last_slot - first_slot) >> align_shift) + 1;
}
/*
* The UEFI memory descriptors have a virtual address field that is only used
* when installing the virtual mapping using SetVirtualAddressMap(). Since it
* is unused here, we can reuse it to keep track of each descriptor's slot
* count.
*/
#define MD_NUM_SLOTS(md) ((md)->virt_addr)
efi_status_t efi_random_alloc(unsigned long size,
unsigned long align,
unsigned long *addr,
unsigned long random_seed)
{
unsigned long map_size, desc_size, total_slots = 0, target_slot;
unsigned long buff_size;
efi_status_t status;
efi_memory_desc_t *memory_map;
int map_offset;
struct efi_boot_memmap map;
map.map = &memory_map;
map.map_size = &map_size;
map.desc_size = &desc_size;
map.desc_ver = NULL;
map.key_ptr = NULL;
map.buff_size = &buff_size;
status = efi_get_memory_map(&map);
if (status != EFI_SUCCESS)
return status;
if (align < EFI_ALLOC_ALIGN)
align = EFI_ALLOC_ALIGN;
/* count the suitable slots in each memory map entry */
for (map_offset = 0; map_offset < map_size; map_offset += desc_size) {
efi_memory_desc_t *md = (void *)memory_map + map_offset;
unsigned long slots;
slots = get_entry_num_slots(md, size, ilog2(align));
MD_NUM_SLOTS(md) = slots;
total_slots += slots;
}
/* find a random number between 0 and total_slots */
target_slot = (total_slots * (u16)random_seed) >> 16;
/*
* target_slot is now a value in the range [0, total_slots), and so
* it corresponds with exactly one of the suitable slots we recorded
* when iterating over the memory map the first time around.
*
* So iterate over the memory map again, subtracting the number of
* slots of each entry at each iteration, until we have found the entry
* that covers our chosen slot. Use the residual value of target_slot
* to calculate the randomly chosen address, and allocate it directly
* using EFI_ALLOCATE_ADDRESS.
*/
for (map_offset = 0; map_offset < map_size; map_offset += desc_size) {
efi_memory_desc_t *md = (void *)memory_map + map_offset;
efi_physical_addr_t target;
unsigned long pages;
if (target_slot >= MD_NUM_SLOTS(md)) {
target_slot -= MD_NUM_SLOTS(md);
continue;
}
target = round_up(md->phys_addr, align) + target_slot * align;
pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
EFI_LOADER_DATA, pages, &target);
if (status == EFI_SUCCESS)
*addr = target;
break;
}
efi_bs_call(free_pool, memory_map);
return status;
}

View File

@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/ctype.h>
#include <linux/types.h>
char *skip_spaces(const char *str)
{
while (isspace(*str))
++str;
return (char *)str;
}

View File

@ -6,6 +6,7 @@
* Copyright (C) 1991, 1992 Linus Torvalds * Copyright (C) 1991, 1992 Linus Torvalds
*/ */
#include <linux/ctype.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/string.h> #include <linux/string.h>
@ -56,3 +57,58 @@ int strncmp(const char *cs, const char *ct, size_t count)
return 0; return 0;
} }
#endif #endif
/* Works only for digits and letters, but small and fast */
#define TOLOWER(x) ((x) | 0x20)
static unsigned int simple_guess_base(const char *cp)
{
if (cp[0] == '0') {
if (TOLOWER(cp[1]) == 'x' && isxdigit(cp[2]))
return 16;
else
return 8;
} else {
return 10;
}
}
/**
* simple_strtoull - convert a string to an unsigned long long
* @cp: The start of the string
* @endp: A pointer to the end of the parsed string will be placed here
* @base: The number base to use
*/
unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base)
{
unsigned long long result = 0;
if (!base)
base = simple_guess_base(cp);
if (base == 16 && cp[0] == '0' && TOLOWER(cp[1]) == 'x')
cp += 2;
while (isxdigit(*cp)) {
unsigned int value;
value = isdigit(*cp) ? *cp - '0' : TOLOWER(*cp) - 'a' + 10;
if (value >= base)
break;
result = result * base + value;
cp++;
}
if (endp)
*endp = (char *)cp;
return result;
}
long simple_strtol(const char *cp, char **endp, unsigned int base)
{
if (*cp == '-')
return -simple_strtoull(cp + 1, endp, base);
return simple_strtoull(cp, endp, base);
}

View File

@ -6,8 +6,6 @@
* *
* ----------------------------------------------------------------------- */ * ----------------------------------------------------------------------- */
#pragma GCC visibility push(hidden)
#include <linux/efi.h> #include <linux/efi.h>
#include <linux/pci.h> #include <linux/pci.h>
@ -17,11 +15,14 @@
#include <asm/desc.h> #include <asm/desc.h>
#include <asm/boot.h> #include <asm/boot.h>
#include "../string.h" #include "efistub.h"
#include "eboot.h"
/* Maximum physical address for 64-bit kernel with 4-level paging */
#define MAXMEM_X86_64_4LEVEL (1ull << 46)
static efi_system_table_t *sys_table; static efi_system_table_t *sys_table;
extern const bool efi_is64; extern const bool efi_is64;
extern u32 image_offset;
__pure efi_system_table_t *efi_system_table(void) __pure efi_system_table_t *efi_system_table(void)
{ {
@ -315,7 +316,7 @@ setup_uga(struct screen_info *si, efi_guid_t *uga_proto, unsigned long size)
return status; return status;
} }
void setup_graphics(struct boot_params *boot_params) static void setup_graphics(struct boot_params *boot_params)
{ {
efi_guid_t graphics_proto = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; efi_guid_t graphics_proto = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
struct screen_info *si; struct screen_info *si;
@ -343,6 +344,14 @@ void setup_graphics(struct boot_params *boot_params)
} }
} }
static void __noreturn efi_exit(efi_handle_t handle, efi_status_t status)
{
efi_bs_call(exit, handle, status, 0, NULL);
for(;;)
asm("hlt");
}
void startup_32(struct boot_params *boot_params); void startup_32(struct boot_params *boot_params);
void __noreturn efi_stub_entry(efi_handle_t handle, void __noreturn efi_stub_entry(efi_handle_t handle,
@ -358,9 +367,9 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
efi_system_table_t *sys_table_arg) efi_system_table_t *sys_table_arg)
{ {
struct boot_params *boot_params; struct boot_params *boot_params;
struct apm_bios_info *bi;
struct setup_header *hdr; struct setup_header *hdr;
efi_loaded_image_t *image; efi_loaded_image_t *image;
void *image_base;
efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID; efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
int options_size = 0; int options_size = 0;
efi_status_t status; efi_status_t status;
@ -372,27 +381,31 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
/* 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)
return EFI_INVALID_PARAMETER; efi_exit(handle, EFI_INVALID_PARAMETER);
status = efi_bs_call(handle_protocol, handle, &proto, (void *)&image); status = efi_bs_call(handle_protocol, handle, &proto, (void **)&image);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
efi_printk("Failed to get handle for LOADED_IMAGE_PROTOCOL\n"); efi_printk("Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
return status; efi_exit(handle, status);
} }
status = efi_low_alloc(0x4000, 1, (unsigned long *)&boot_params); image_base = efi_table_attr(image, image_base);
image_offset = (void *)startup_32 - image_base;
hdr = &((struct boot_params *)image_base)->hdr;
status = efi_allocate_pages(0x4000, (unsigned long *)&boot_params, ULONG_MAX);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
efi_printk("Failed to allocate lowmem for boot params\n"); efi_printk("Failed to allocate lowmem for boot params\n");
return status; efi_exit(handle, status);
} }
memset(boot_params, 0x0, 0x4000); memset(boot_params, 0x0, 0x4000);
hdr = &boot_params->hdr; hdr = &boot_params->hdr;
bi = &boot_params->apm_bios_info;
/* Copy the second sector to boot_params */ /* Copy the second sector to boot_params */
memcpy(&hdr->jump, image->image_base + 512, 512); memcpy(&hdr->jump, image_base + 512, 512);
/* /*
* Fill out some of the header fields ourselves because the * Fill out some of the header fields ourselves because the
@ -405,7 +418,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
hdr->type_of_loader = 0x21; hdr->type_of_loader = 0x21;
/* Convert unicode cmdline to ascii */ /* Convert unicode cmdline to ascii */
cmdline_ptr = efi_convert_cmdline(image, &options_size); cmdline_ptr = efi_convert_cmdline(image, &options_size, ULONG_MAX);
if (!cmdline_ptr) if (!cmdline_ptr)
goto fail; goto fail;
@ -416,45 +429,34 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
hdr->ramdisk_image = 0; hdr->ramdisk_image = 0;
hdr->ramdisk_size = 0; hdr->ramdisk_size = 0;
/* Clear APM BIOS info */ if (efi_is_native()) {
memset(bi, 0, sizeof(*bi)); status = efi_parse_options(cmdline_ptr);
if (status != EFI_SUCCESS)
goto fail2;
status = efi_parse_options(cmdline_ptr); if (!noinitrd()) {
if (status != EFI_SUCCESS) status = efi_load_initrd(image, &ramdisk_addr,
goto fail2; &ramdisk_size,
hdr->initrd_addr_max,
status = handle_cmdline_files(image, ULONG_MAX);
(char *)(unsigned long)hdr->cmd_line_ptr, if (status != EFI_SUCCESS)
"initrd=", hdr->initrd_addr_max, goto fail2;
&ramdisk_addr, &ramdisk_size); hdr->ramdisk_image = ramdisk_addr & 0xffffffff;
hdr->ramdisk_size = ramdisk_size & 0xffffffff;
if (status != EFI_SUCCESS && boot_params->ext_ramdisk_image = (u64)ramdisk_addr >> 32;
hdr->xloadflags & XLF_CAN_BE_LOADED_ABOVE_4G) { boot_params->ext_ramdisk_size = (u64)ramdisk_size >> 32;
efi_printk("Trying to load files to higher address\n"); }
status = handle_cmdline_files(image,
(char *)(unsigned long)hdr->cmd_line_ptr,
"initrd=", -1UL,
&ramdisk_addr, &ramdisk_size);
} }
if (status != EFI_SUCCESS)
goto fail2;
hdr->ramdisk_image = ramdisk_addr & 0xffffffff;
hdr->ramdisk_size = ramdisk_size & 0xffffffff;
boot_params->ext_ramdisk_image = (u64)ramdisk_addr >> 32;
boot_params->ext_ramdisk_size = (u64)ramdisk_size >> 32;
hdr->code32_start = (u32)(unsigned long)startup_32;
efi_stub_entry(handle, sys_table, boot_params); efi_stub_entry(handle, sys_table, boot_params);
/* not reached */ /* not reached */
fail2: fail2:
efi_free(options_size, hdr->cmd_line_ptr); efi_free(options_size, (unsigned long)cmdline_ptr);
fail: fail:
efi_free(0x4000, (unsigned long)boot_params); efi_free(0x4000, (unsigned long)boot_params);
return status; efi_exit(handle, status);
} }
static void add_e820ext(struct boot_params *params, static void add_e820ext(struct boot_params *params,
@ -705,27 +707,73 @@ static efi_status_t exit_boot(struct boot_params *boot_params, void *handle)
} }
/* /*
* On success we return a pointer to a boot_params structure, and NULL * On success, we return the address of startup_32, which has potentially been
* on failure. * relocated by efi_relocate_kernel.
* On failure, we exit to the firmware via efi_exit instead of returning.
*/ */
struct boot_params *efi_main(efi_handle_t handle, unsigned long efi_main(efi_handle_t handle,
efi_system_table_t *sys_table_arg, efi_system_table_t *sys_table_arg,
struct boot_params *boot_params) struct boot_params *boot_params)
{ {
struct desc_ptr *gdt = NULL; unsigned long bzimage_addr = (unsigned long)startup_32;
unsigned long buffer_start, buffer_end;
struct setup_header *hdr = &boot_params->hdr; struct setup_header *hdr = &boot_params->hdr;
efi_status_t status; efi_status_t status;
struct desc_struct *desc;
unsigned long cmdline_paddr; unsigned long cmdline_paddr;
sys_table = sys_table_arg; sys_table = sys_table_arg;
/* 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)
goto fail; efi_exit(handle, EFI_INVALID_PARAMETER);
/* /*
* make_boot_params() may have been called before efi_main(), in which * If the kernel isn't already loaded at a suitable address,
* relocate it.
*
* It must be loaded above LOAD_PHYSICAL_ADDR.
*
* The maximum address for 64-bit is 1 << 46 for 4-level paging. This
* is defined as the macro MAXMEM, but unfortunately that is not a
* compile-time constant if 5-level paging is configured, so we instead
* define our own macro for use here.
*
* For 32-bit, the maximum address is complicated to figure out, for
* now use KERNEL_IMAGE_SIZE, which will be 512MiB, the same as what
* KASLR uses.
*
* Also relocate it if image_offset is zero, i.e. we weren't loaded by
* LoadImage, but we are not aligned correctly.
*/
buffer_start = ALIGN(bzimage_addr - image_offset,
hdr->kernel_alignment);
buffer_end = buffer_start + hdr->init_size;
if ((buffer_start < LOAD_PHYSICAL_ADDR) ||
(IS_ENABLED(CONFIG_X86_32) && buffer_end > KERNEL_IMAGE_SIZE) ||
(IS_ENABLED(CONFIG_X86_64) && buffer_end > MAXMEM_X86_64_4LEVEL) ||
(image_offset == 0 && !IS_ALIGNED(bzimage_addr,
hdr->kernel_alignment))) {
status = efi_relocate_kernel(&bzimage_addr,
hdr->init_size, hdr->init_size,
hdr->pref_address,
hdr->kernel_alignment,
LOAD_PHYSICAL_ADDR);
if (status != EFI_SUCCESS) {
efi_printk("efi_relocate_kernel() failed!\n");
goto fail;
}
/*
* Now that we've copied the kernel elsewhere, we no longer
* have a set up block before startup_32(), so reset image_offset
* to zero in case it was set earlier.
*/
image_offset = 0;
}
/*
* efi_pe_entry() may have been called before efi_main(), in which
* case this is the second time we parse the cmdline. This is ok, * case this is the second time we parse the cmdline. This is ok,
* parsing the cmdline multiple times does not have side-effects. * parsing the cmdline multiple times does not have side-effects.
*/ */
@ -733,6 +781,28 @@ struct boot_params *efi_main(efi_handle_t handle,
((u64)boot_params->ext_cmd_line_ptr << 32)); ((u64)boot_params->ext_cmd_line_ptr << 32));
efi_parse_options((char *)cmdline_paddr); efi_parse_options((char *)cmdline_paddr);
/*
* At this point, an initrd may already have been loaded, either by
* the bootloader and passed via bootparams, or loaded from a initrd=
* command line option by efi_pe_entry() above. In either case, we
* permit an initrd loaded from the LINUX_EFI_INITRD_MEDIA_GUID device
* path to supersede it.
*/
if (!noinitrd()) {
unsigned long addr, size;
status = efi_load_initrd_dev_path(&addr, &size, ULONG_MAX);
if (status == EFI_SUCCESS) {
hdr->ramdisk_image = (u32)addr;
hdr->ramdisk_size = (u32)size;
boot_params->ext_ramdisk_image = (u64)addr >> 32;
boot_params->ext_ramdisk_size = (u64)size >> 32;
} else if (status != EFI_NOT_FOUND) {
efi_printk("efi_load_initrd_dev_path() failed!\n");
goto fail;
}
}
/* /*
* If the boot loader gave us a value for secure_boot then we use that, * If the boot loader gave us a value for secure_boot then we use that,
* otherwise we ask the BIOS. * otherwise we ask the BIOS.
@ -753,137 +823,15 @@ struct boot_params *efi_main(efi_handle_t handle,
setup_quirks(boot_params); setup_quirks(boot_params);
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, sizeof(*gdt),
(void **)&gdt);
if (status != EFI_SUCCESS) {
efi_printk("Failed to allocate memory for 'gdt' structure\n");
goto fail;
}
gdt->size = 0x800;
status = efi_low_alloc(gdt->size, 8, (unsigned long *)&gdt->address);
if (status != EFI_SUCCESS) {
efi_printk("Failed to allocate memory for 'gdt'\n");
goto fail;
}
/*
* If the kernel isn't already loaded at the preferred load
* address, relocate it.
*/
if (hdr->pref_address != hdr->code32_start) {
unsigned long bzimage_addr = hdr->code32_start;
status = efi_relocate_kernel(&bzimage_addr,
hdr->init_size, hdr->init_size,
hdr->pref_address,
hdr->kernel_alignment,
LOAD_PHYSICAL_ADDR);
if (status != EFI_SUCCESS) {
efi_printk("efi_relocate_kernel() failed!\n");
goto fail;
}
hdr->pref_address = hdr->code32_start;
hdr->code32_start = bzimage_addr;
}
status = exit_boot(boot_params, handle); status = exit_boot(boot_params, handle);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
efi_printk("exit_boot() failed!\n"); efi_printk("exit_boot() failed!\n");
goto fail; goto fail;
} }
memset((char *)gdt->address, 0x0, gdt->size); return bzimage_addr;
desc = (struct desc_struct *)gdt->address;
/* The first GDT is a dummy. */
desc++;
if (IS_ENABLED(CONFIG_X86_64)) {
/* __KERNEL32_CS */
desc->limit0 = 0xffff;
desc->base0 = 0x0000;
desc->base1 = 0x0000;
desc->type = SEG_TYPE_CODE | SEG_TYPE_EXEC_READ;
desc->s = DESC_TYPE_CODE_DATA;
desc->dpl = 0;
desc->p = 1;
desc->limit1 = 0xf;
desc->avl = 0;
desc->l = 0;
desc->d = SEG_OP_SIZE_32BIT;
desc->g = SEG_GRANULARITY_4KB;
desc->base2 = 0x00;
desc++;
} else {
/* Second entry is unused on 32-bit */
desc++;
}
/* __KERNEL_CS */
desc->limit0 = 0xffff;
desc->base0 = 0x0000;
desc->base1 = 0x0000;
desc->type = SEG_TYPE_CODE | SEG_TYPE_EXEC_READ;
desc->s = DESC_TYPE_CODE_DATA;
desc->dpl = 0;
desc->p = 1;
desc->limit1 = 0xf;
desc->avl = 0;
if (IS_ENABLED(CONFIG_X86_64)) {
desc->l = 1;
desc->d = 0;
} else {
desc->l = 0;
desc->d = SEG_OP_SIZE_32BIT;
}
desc->g = SEG_GRANULARITY_4KB;
desc->base2 = 0x00;
desc++;
/* __KERNEL_DS */
desc->limit0 = 0xffff;
desc->base0 = 0x0000;
desc->base1 = 0x0000;
desc->type = SEG_TYPE_DATA | SEG_TYPE_READ_WRITE;
desc->s = DESC_TYPE_CODE_DATA;
desc->dpl = 0;
desc->p = 1;
desc->limit1 = 0xf;
desc->avl = 0;
desc->l = 0;
desc->d = SEG_OP_SIZE_32BIT;
desc->g = SEG_GRANULARITY_4KB;
desc->base2 = 0x00;
desc++;
if (IS_ENABLED(CONFIG_X86_64)) {
/* Task segment value */
desc->limit0 = 0x0000;
desc->base0 = 0x0000;
desc->base1 = 0x0000;
desc->type = SEG_TYPE_TSS;
desc->s = 0;
desc->dpl = 0;
desc->p = 1;
desc->limit1 = 0x0;
desc->avl = 0;
desc->l = 0;
desc->d = 0;
desc->g = SEG_GRANULARITY_4KB;
desc->base2 = 0x00;
desc++;
}
asm volatile("cli");
asm volatile ("lgdt %0" : : "m" (*gdt));
return boot_params;
fail: fail:
efi_printk("efi_main() failed!\n"); efi_printk("efi_main() failed!\n");
for (;;) efi_exit(handle, status);
asm("hlt");
} }

View File

@ -13,6 +13,7 @@
#include <asm/early_ioremap.h> #include <asm/early_ioremap.h>
static int __initdata tbl_size; static int __initdata tbl_size;
unsigned long __ro_after_init efi_mem_attr_table = EFI_INVALID_TABLE_ADDR;
/* /*
* Reserve the memory associated with the Memory Attributes configuration * Reserve the memory associated with the Memory Attributes configuration
@ -22,13 +23,13 @@ int __init efi_memattr_init(void)
{ {
efi_memory_attributes_table_t *tbl; efi_memory_attributes_table_t *tbl;
if (efi.mem_attr_table == EFI_INVALID_TABLE_ADDR) if (efi_mem_attr_table == EFI_INVALID_TABLE_ADDR)
return 0; return 0;
tbl = early_memremap(efi.mem_attr_table, sizeof(*tbl)); tbl = early_memremap(efi_mem_attr_table, sizeof(*tbl));
if (!tbl) { if (!tbl) {
pr_err("Failed to map EFI Memory Attributes table @ 0x%lx\n", pr_err("Failed to map EFI Memory Attributes table @ 0x%lx\n",
efi.mem_attr_table); efi_mem_attr_table);
return -ENOMEM; return -ENOMEM;
} }
@ -39,7 +40,7 @@ int __init efi_memattr_init(void)
} }
tbl_size = sizeof(*tbl) + tbl->num_entries * tbl->desc_size; tbl_size = sizeof(*tbl) + tbl->num_entries * tbl->desc_size;
memblock_reserve(efi.mem_attr_table, tbl_size); memblock_reserve(efi_mem_attr_table, tbl_size);
set_bit(EFI_MEM_ATTR, &efi.flags); set_bit(EFI_MEM_ATTR, &efi.flags);
unmap: unmap:
@ -147,10 +148,10 @@ int __init efi_memattr_apply_permissions(struct mm_struct *mm,
if (WARN_ON(!efi_enabled(EFI_MEMMAP))) if (WARN_ON(!efi_enabled(EFI_MEMMAP)))
return 0; return 0;
tbl = memremap(efi.mem_attr_table, tbl_size, MEMREMAP_WB); tbl = memremap(efi_mem_attr_table, tbl_size, MEMREMAP_WB);
if (!tbl) { if (!tbl) {
pr_err("Failed to map EFI Memory Attributes table @ 0x%lx\n", pr_err("Failed to map EFI Memory Attributes table @ 0x%lx\n",
efi.mem_attr_table); efi_mem_attr_table);
return -ENOMEM; return -ENOMEM;
} }

View File

@ -15,7 +15,7 @@ void efi_reboot(enum reboot_mode reboot_mode, const char *__unused)
const char *str[] = { "cold", "warm", "shutdown", "platform" }; const char *str[] = { "cold", "warm", "shutdown", "platform" };
int efi_mode, cap_reset_mode; int efi_mode, cap_reset_mode;
if (!efi_enabled(EFI_RUNTIME_SERVICES)) if (!efi_rt_services_supported(EFI_RT_SUPPORTED_RESET_SYSTEM))
return; return;
switch (reboot_mode) { switch (reboot_mode) {
@ -64,7 +64,7 @@ static void efi_power_off(void)
static int __init efi_shutdown_init(void) static int __init efi_shutdown_init(void)
{ {
if (!efi_enabled(EFI_RUNTIME_SERVICES)) if (!efi_rt_services_supported(EFI_RT_SUPPORTED_RESET_SYSTEM))
return -ENODEV; return -ENODEV;
if (efi_poweroff_required()) { if (efi_poweroff_required()) {

View File

@ -40,9 +40,9 @@
* code doesn't get too cluttered: * code doesn't get too cluttered:
*/ */
#define efi_call_virt(f, args...) \ #define efi_call_virt(f, args...) \
efi_call_virt_pointer(efi.systab->runtime, f, args) efi_call_virt_pointer(efi.runtime, f, args)
#define __efi_call_virt(f, args...) \ #define __efi_call_virt(f, args...) \
__efi_call_virt_pointer(efi.systab->runtime, f, args) __efi_call_virt_pointer(efi.runtime, f, args)
struct efi_runtime_work efi_rts_work; struct efi_runtime_work efi_rts_work;

View File

@ -1071,7 +1071,7 @@ EXPORT_SYMBOL_GPL(efivar_entry_iter_end);
* entry on the list. It is safe for @func to remove entries in the * entry on the list. It is safe for @func to remove entries in the
* list via efivar_entry_delete(). * list via efivar_entry_delete().
* *
* You MUST call efivar_enter_iter_begin() before this function, and * You MUST call efivar_entry_iter_begin() before this function, and
* efivar_entry_iter_end() afterwards. * efivar_entry_iter_end() afterwards.
* *
* It is possible to begin iteration from an arbitrary entry within * It is possible to begin iteration from an arbitrary entry within

View File

@ -80,6 +80,8 @@ setup_vga_console(struct pcdp_device *dev)
#endif #endif
} }
extern unsigned long hcdp_phys;
int __init int __init
efi_setup_pcdp_console(char *cmdline) efi_setup_pcdp_console(char *cmdline)
{ {
@ -89,11 +91,11 @@ efi_setup_pcdp_console(char *cmdline)
int i, serial = 0; int i, serial = 0;
int rc = -ENODEV; int rc = -ENODEV;
if (efi.hcdp == EFI_INVALID_TABLE_ADDR) if (hcdp_phys == EFI_INVALID_TABLE_ADDR)
return -ENODEV; return -ENODEV;
pcdp = early_memremap(efi.hcdp, 4096); pcdp = early_memremap(hcdp_phys, 4096);
printk(KERN_INFO "PCDP: v%d at 0x%lx\n", pcdp->rev, efi.hcdp); printk(KERN_INFO "PCDP: v%d at 0x%lx\n", pcdp->rev, hcdp_phys);
if (strstr(cmdline, "console=hcdp")) { if (strstr(cmdline, "console=hcdp")) {
if (pcdp->rev < 3) if (pcdp->rev < 3)

View File

@ -78,7 +78,7 @@ static int read_efi_var(const char *name, unsigned long *size,
*size = 0; *size = 0;
*return_data = NULL; *return_data = NULL;
if (!efi_enabled(EFI_RUNTIME_SERVICES)) if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
return -EOPNOTSUPP; return -EOPNOTSUPP;
uni_name = kcalloc(strlen(name) + 1, sizeof(efi_char16_t), GFP_KERNEL); uni_name = kcalloc(strlen(name) + 1, sizeof(efi_char16_t), GFP_KERNEL);

View File

@ -12,10 +12,6 @@ obj-$(CONFIG_RTC_CLASS) += rtc-core.o
obj-$(CONFIG_RTC_MC146818_LIB) += rtc-mc146818-lib.o obj-$(CONFIG_RTC_MC146818_LIB) += rtc-mc146818-lib.o
rtc-core-y := class.o interface.o rtc-core-y := class.o interface.o
ifdef CONFIG_RTC_DRV_EFI
rtc-core-y += rtc-efi-platform.o
endif
rtc-core-$(CONFIG_RTC_NVMEM) += nvmem.o rtc-core-$(CONFIG_RTC_NVMEM) += nvmem.o
rtc-core-$(CONFIG_RTC_INTF_DEV) += dev.o rtc-core-$(CONFIG_RTC_INTF_DEV) += dev.o
rtc-core-$(CONFIG_RTC_INTF_PROC) += proc.o rtc-core-$(CONFIG_RTC_INTF_PROC) += proc.o

View File

@ -1,35 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Moved from arch/ia64/kernel/time.c
*
* Copyright (C) 1998-2003 Hewlett-Packard Co
* Stephane Eranian <eranian@hpl.hp.com>
* David Mosberger <davidm@hpl.hp.com>
* Copyright (C) 1999 Don Dugger <don.dugger@intel.com>
* Copyright (C) 1999-2000 VA Linux Systems
* Copyright (C) 1999-2000 Walt Drummond <drummond@valinux.com>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/efi.h>
#include <linux/platform_device.h>
static struct platform_device rtc_efi_dev = {
.name = "rtc-efi",
.id = -1,
};
static int __init rtc_init(void)
{
if (efi_enabled(EFI_RUNTIME_SERVICES))
if (platform_device_register(&rtc_efi_dev) < 0)
pr_err("unable to register rtc device...\n");
/* not necessarily an error */
return 0;
}
module_init(rtc_init);

View File

@ -621,7 +621,7 @@ static int isci_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return -ENOMEM; return -ENOMEM;
pci_set_drvdata(pdev, pci_info); pci_set_drvdata(pdev, pci_info);
if (efi_enabled(EFI_RUNTIME_SERVICES)) if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
orom = isci_get_efi_var(pdev); orom = isci_get_efi_var(pdev);
if (!orom) if (!orom)

View File

@ -252,7 +252,7 @@ static struct file_system_type efivarfs_type = {
static __init int efivarfs_init(void) static __init int efivarfs_init(void)
{ {
if (!efi_enabled(EFI_RUNTIME_SERVICES)) if (!efi_rt_services_supported(EFI_RT_SUPPORTED_VARIABLE_SERVICES))
return -ENODEV; return -ENODEV;
if (!efivars_kobject()) if (!efivars_kobject())

View File

@ -56,19 +56,6 @@ typedef void *efi_handle_t;
#define __efiapi #define __efiapi
#endif #endif
#define efi_get_handle_at(array, idx) \
(efi_is_native() ? (array)[idx] \
: (efi_handle_t)(unsigned long)((u32 *)(array))[idx])
#define efi_get_handle_num(size) \
((size) / (efi_is_native() ? sizeof(efi_handle_t) : sizeof(u32)))
#define for_each_efi_handle(handle, array, size, i) \
for (i = 0; \
i < efi_get_handle_num(size) && \
((handle = efi_get_handle_at((array), i)) || true); \
i++)
/* /*
* The UEFI spec and EDK2 reference implementation both define EFI_GUID as * The UEFI spec and EDK2 reference implementation both define EFI_GUID as
* struct { u32 a; u16; b; u16 c; u8 d[8]; }; and so the implied alignment * struct { u32 a; u16; b; u16 c; u8 d[8]; }; and so the implied alignment
@ -157,15 +144,6 @@ typedef struct {
u32 imagesize; u32 imagesize;
} efi_capsule_header_t; } efi_capsule_header_t;
struct efi_boot_memmap {
efi_memory_desc_t **map;
unsigned long *map_size;
unsigned long *desc_size;
u32 *desc_ver;
unsigned long *key_ptr;
unsigned long *buff_size;
};
/* /*
* EFI capsule flags * EFI capsule flags
*/ */
@ -187,14 +165,6 @@ struct capsule_info {
int __efi_capsule_setup_info(struct capsule_info *cap_info); int __efi_capsule_setup_info(struct capsule_info *cap_info);
/*
* Allocation types for calls to boottime->allocate_pages.
*/
#define EFI_ALLOCATE_ANY_PAGES 0
#define EFI_ALLOCATE_MAX_ADDRESS 1
#define EFI_ALLOCATE_ADDRESS 2
#define EFI_MAX_ALLOCATE_TYPE 3
typedef int (*efi_freemem_callback_t) (u64 start, u64 end, void *arg); typedef int (*efi_freemem_callback_t) (u64 start, u64 end, void *arg);
/* /*
@ -224,291 +194,7 @@ typedef struct {
u8 sets_to_zero; u8 sets_to_zero;
} efi_time_cap_t; } efi_time_cap_t;
typedef struct { typedef union efi_boot_services efi_boot_services_t;
efi_table_hdr_t hdr;
u32 raise_tpl;
u32 restore_tpl;
u32 allocate_pages;
u32 free_pages;
u32 get_memory_map;
u32 allocate_pool;
u32 free_pool;
u32 create_event;
u32 set_timer;
u32 wait_for_event;
u32 signal_event;
u32 close_event;
u32 check_event;
u32 install_protocol_interface;
u32 reinstall_protocol_interface;
u32 uninstall_protocol_interface;
u32 handle_protocol;
u32 __reserved;
u32 register_protocol_notify;
u32 locate_handle;
u32 locate_device_path;
u32 install_configuration_table;
u32 load_image;
u32 start_image;
u32 exit;
u32 unload_image;
u32 exit_boot_services;
u32 get_next_monotonic_count;
u32 stall;
u32 set_watchdog_timer;
u32 connect_controller;
u32 disconnect_controller;
u32 open_protocol;
u32 close_protocol;
u32 open_protocol_information;
u32 protocols_per_handle;
u32 locate_handle_buffer;
u32 locate_protocol;
u32 install_multiple_protocol_interfaces;
u32 uninstall_multiple_protocol_interfaces;
u32 calculate_crc32;
u32 copy_mem;
u32 set_mem;
u32 create_event_ex;
} __packed efi_boot_services_32_t;
/*
* EFI Boot Services table
*/
typedef union {
struct {
efi_table_hdr_t hdr;
void *raise_tpl;
void *restore_tpl;
efi_status_t (__efiapi *allocate_pages)(int, int, unsigned long,
efi_physical_addr_t *);
efi_status_t (__efiapi *free_pages)(efi_physical_addr_t,
unsigned long);
efi_status_t (__efiapi *get_memory_map)(unsigned long *, void *,
unsigned long *,
unsigned long *, u32 *);
efi_status_t (__efiapi *allocate_pool)(int, unsigned long,
void **);
efi_status_t (__efiapi *free_pool)(void *);
void *create_event;
void *set_timer;
void *wait_for_event;
void *signal_event;
void *close_event;
void *check_event;
void *install_protocol_interface;
void *reinstall_protocol_interface;
void *uninstall_protocol_interface;
efi_status_t (__efiapi *handle_protocol)(efi_handle_t,
efi_guid_t *, void **);
void *__reserved;
void *register_protocol_notify;
efi_status_t (__efiapi *locate_handle)(int, efi_guid_t *,
void *, unsigned long *,
efi_handle_t *);
void *locate_device_path;
efi_status_t (__efiapi *install_configuration_table)(efi_guid_t *,
void *);
void *load_image;
void *start_image;
void *exit;
void *unload_image;
efi_status_t (__efiapi *exit_boot_services)(efi_handle_t,
unsigned long);
void *get_next_monotonic_count;
void *stall;
void *set_watchdog_timer;
void *connect_controller;
efi_status_t (__efiapi *disconnect_controller)(efi_handle_t,
efi_handle_t,
efi_handle_t);
void *open_protocol;
void *close_protocol;
void *open_protocol_information;
void *protocols_per_handle;
void *locate_handle_buffer;
efi_status_t (__efiapi *locate_protocol)(efi_guid_t *, void *,
void **);
void *install_multiple_protocol_interfaces;
void *uninstall_multiple_protocol_interfaces;
void *calculate_crc32;
void *copy_mem;
void *set_mem;
void *create_event_ex;
};
efi_boot_services_32_t mixed_mode;
} efi_boot_services_t;
typedef enum {
EfiPciIoWidthUint8,
EfiPciIoWidthUint16,
EfiPciIoWidthUint32,
EfiPciIoWidthUint64,
EfiPciIoWidthFifoUint8,
EfiPciIoWidthFifoUint16,
EfiPciIoWidthFifoUint32,
EfiPciIoWidthFifoUint64,
EfiPciIoWidthFillUint8,
EfiPciIoWidthFillUint16,
EfiPciIoWidthFillUint32,
EfiPciIoWidthFillUint64,
EfiPciIoWidthMaximum
} EFI_PCI_IO_PROTOCOL_WIDTH;
typedef enum {
EfiPciIoAttributeOperationGet,
EfiPciIoAttributeOperationSet,
EfiPciIoAttributeOperationEnable,
EfiPciIoAttributeOperationDisable,
EfiPciIoAttributeOperationSupported,
EfiPciIoAttributeOperationMaximum
} EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION;
typedef struct {
u32 read;
u32 write;
} efi_pci_io_protocol_access_32_t;
typedef union efi_pci_io_protocol efi_pci_io_protocol_t;
typedef
efi_status_t (__efiapi *efi_pci_io_protocol_cfg_t)(efi_pci_io_protocol_t *,
EFI_PCI_IO_PROTOCOL_WIDTH,
u32 offset,
unsigned long count,
void *buffer);
typedef struct {
void *read;
void *write;
} efi_pci_io_protocol_access_t;
typedef struct {
efi_pci_io_protocol_cfg_t read;
efi_pci_io_protocol_cfg_t write;
} efi_pci_io_protocol_config_access_t;
union efi_pci_io_protocol {
struct {
void *poll_mem;
void *poll_io;
efi_pci_io_protocol_access_t mem;
efi_pci_io_protocol_access_t io;
efi_pci_io_protocol_config_access_t pci;
void *copy_mem;
void *map;
void *unmap;
void *allocate_buffer;
void *free_buffer;
void *flush;
efi_status_t (__efiapi *get_location)(efi_pci_io_protocol_t *,
unsigned long *segment_nr,
unsigned long *bus_nr,
unsigned long *device_nr,
unsigned long *func_nr);
void *attributes;
void *get_bar_attributes;
void *set_bar_attributes;
uint64_t romsize;
void *romimage;
};
struct {
u32 poll_mem;
u32 poll_io;
efi_pci_io_protocol_access_32_t mem;
efi_pci_io_protocol_access_32_t io;
efi_pci_io_protocol_access_32_t pci;
u32 copy_mem;
u32 map;
u32 unmap;
u32 allocate_buffer;
u32 free_buffer;
u32 flush;
u32 get_location;
u32 attributes;
u32 get_bar_attributes;
u32 set_bar_attributes;
u64 romsize;
u32 romimage;
} mixed_mode;
};
#define EFI_PCI_IO_ATTRIBUTE_ISA_MOTHERBOARD_IO 0x0001
#define EFI_PCI_IO_ATTRIBUTE_ISA_IO 0x0002
#define EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO 0x0004
#define EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY 0x0008
#define EFI_PCI_IO_ATTRIBUTE_VGA_IO 0x0010
#define EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO 0x0020
#define EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO 0x0040
#define EFI_PCI_IO_ATTRIBUTE_MEMORY_WRITE_COMBINE 0x0080
#define EFI_PCI_IO_ATTRIBUTE_IO 0x0100
#define EFI_PCI_IO_ATTRIBUTE_MEMORY 0x0200
#define EFI_PCI_IO_ATTRIBUTE_BUS_MASTER 0x0400
#define EFI_PCI_IO_ATTRIBUTE_MEMORY_CACHED 0x0800
#define EFI_PCI_IO_ATTRIBUTE_MEMORY_DISABLE 0x1000
#define EFI_PCI_IO_ATTRIBUTE_EMBEDDED_DEVICE 0x2000
#define EFI_PCI_IO_ATTRIBUTE_EMBEDDED_ROM 0x4000
#define EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE 0x8000
#define EFI_PCI_IO_ATTRIBUTE_ISA_IO_16 0x10000
#define EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO_16 0x20000
#define EFI_PCI_IO_ATTRIBUTE_VGA_IO_16 0x40000
struct efi_dev_path;
typedef union apple_properties_protocol apple_properties_protocol_t;
union apple_properties_protocol {
struct {
unsigned long version;
efi_status_t (__efiapi *get)(apple_properties_protocol_t *,
struct efi_dev_path *,
efi_char16_t *, void *, u32 *);
efi_status_t (__efiapi *set)(apple_properties_protocol_t *,
struct efi_dev_path *,
efi_char16_t *, void *, u32);
efi_status_t (__efiapi *del)(apple_properties_protocol_t *,
struct efi_dev_path *,
efi_char16_t *);
efi_status_t (__efiapi *get_all)(apple_properties_protocol_t *,
void *buffer, u32 *);
};
struct {
u32 version;
u32 get;
u32 set;
u32 del;
u32 get_all;
} mixed_mode;
};
typedef u32 efi_tcg2_event_log_format;
typedef union efi_tcg2_protocol efi_tcg2_protocol_t;
union efi_tcg2_protocol {
struct {
void *get_capability;
efi_status_t (__efiapi *get_event_log)(efi_handle_t,
efi_tcg2_event_log_format,
efi_physical_addr_t *,
efi_physical_addr_t *,
efi_bool_t *);
void *hash_log_extend_event;
void *submit_command;
void *get_active_pcr_banks;
void *set_active_pcr_banks;
void *get_result_of_set_active_pcr_banks;
};
struct {
u32 get_capability;
u32 get_event_log;
u32 hash_log_extend_event;
u32 submit_command;
u32 get_active_pcr_banks;
u32 set_active_pcr_banks;
u32 get_result_of_set_active_pcr_banks;
} mixed_mode;
};
/* /*
* Types and defines for EFI ResetSystem * Types and defines for EFI ResetSystem
@ -646,6 +332,9 @@ void efi_native_runtime_setup(void);
#define EFI_CONSOLE_OUT_DEVICE_GUID EFI_GUID(0xd3b36f2c, 0xd551, 0x11d4, 0x9a, 0x46, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d) #define EFI_CONSOLE_OUT_DEVICE_GUID EFI_GUID(0xd3b36f2c, 0xd551, 0x11d4, 0x9a, 0x46, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d)
#define APPLE_PROPERTIES_PROTOCOL_GUID EFI_GUID(0x91bd12fe, 0xf6c3, 0x44fb, 0xa5, 0xb7, 0x51, 0x22, 0xab, 0x30, 0x3a, 0xe0) #define APPLE_PROPERTIES_PROTOCOL_GUID EFI_GUID(0x91bd12fe, 0xf6c3, 0x44fb, 0xa5, 0xb7, 0x51, 0x22, 0xab, 0x30, 0x3a, 0xe0)
#define EFI_TCG2_PROTOCOL_GUID EFI_GUID(0x607f766c, 0x7455, 0x42be, 0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f) #define EFI_TCG2_PROTOCOL_GUID EFI_GUID(0x607f766c, 0x7455, 0x42be, 0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f)
#define EFI_LOAD_FILE_PROTOCOL_GUID EFI_GUID(0x56ec3091, 0x954c, 0x11d2, 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
#define EFI_LOAD_FILE2_PROTOCOL_GUID EFI_GUID(0x4006c0c1, 0xfcb3, 0x403e, 0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d)
#define EFI_RT_PROPERTIES_TABLE_GUID EFI_GUID(0xeb66918a, 0x7eef, 0x402a, 0x84, 0x2e, 0x93, 0x1d, 0x21, 0xc3, 0x8a, 0xe9)
#define EFI_IMAGE_SECURITY_DATABASE_GUID EFI_GUID(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f) #define EFI_IMAGE_SECURITY_DATABASE_GUID EFI_GUID(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f)
#define EFI_SHIM_LOCK_GUID EFI_GUID(0x605dab50, 0xe046, 0x4300, 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23) #define EFI_SHIM_LOCK_GUID EFI_GUID(0x605dab50, 0xe046, 0x4300, 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23)
@ -665,6 +354,7 @@ void efi_native_runtime_setup(void);
#define LINUX_EFI_TPM_EVENT_LOG_GUID EFI_GUID(0xb7799cb0, 0xeca2, 0x4943, 0x96, 0x67, 0x1f, 0xae, 0x07, 0xb7, 0x47, 0xfa) #define LINUX_EFI_TPM_EVENT_LOG_GUID EFI_GUID(0xb7799cb0, 0xeca2, 0x4943, 0x96, 0x67, 0x1f, 0xae, 0x07, 0xb7, 0x47, 0xfa)
#define LINUX_EFI_TPM_FINAL_LOG_GUID EFI_GUID(0x1e2ed096, 0x30e2, 0x4254, 0xbd, 0x89, 0x86, 0x3b, 0xbe, 0xf8, 0x23, 0x25) #define LINUX_EFI_TPM_FINAL_LOG_GUID EFI_GUID(0x1e2ed096, 0x30e2, 0x4254, 0xbd, 0x89, 0x86, 0x3b, 0xbe, 0xf8, 0x23, 0x25)
#define LINUX_EFI_MEMRESERVE_TABLE_GUID EFI_GUID(0x888eb0c6, 0x8ede, 0x4ff5, 0xa8, 0xf0, 0x9a, 0xee, 0x5c, 0xb9, 0x77, 0xc2) #define LINUX_EFI_MEMRESERVE_TABLE_GUID EFI_GUID(0x888eb0c6, 0x8ede, 0x4ff5, 0xa8, 0xf0, 0x9a, 0xee, 0x5c, 0xb9, 0x77, 0xc2)
#define LINUX_EFI_INITRD_MEDIA_GUID EFI_GUID(0x5568e427, 0x68fc, 0x4f3d, 0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68)
/* OEM GUIDs */ /* OEM GUIDs */
#define DELLEMC_EFI_RCI2_TABLE_GUID EFI_GUID(0x2d9f28a2, 0xa886, 0x456a, 0x97, 0xa8, 0xf1, 0x1e, 0xf2, 0x4f, 0xf4, 0x55) #define DELLEMC_EFI_RCI2_TABLE_GUID EFI_GUID(0x2d9f28a2, 0xa886, 0x456a, 0x97, 0xa8, 0xf1, 0x1e, 0xf2, 0x4f, 0xf4, 0x55)
@ -788,74 +478,6 @@ struct efi_mem_range {
u64 attribute; u64 attribute;
}; };
struct efi_fdt_params {
u64 system_table;
u64 mmap;
u32 mmap_size;
u32 desc_size;
u32 desc_ver;
};
typedef struct {
u32 revision;
efi_handle_t parent_handle;
efi_system_table_t *system_table;
efi_handle_t device_handle;
void *file_path;
void *reserved;
u32 load_options_size;
void *load_options;
void *image_base;
__aligned_u64 image_size;
unsigned int image_code_type;
unsigned int image_data_type;
efi_status_t ( __efiapi *unload)(efi_handle_t image_handle);
} efi_loaded_image_t;
typedef struct {
u64 size;
u64 file_size;
u64 phys_size;
efi_time_t create_time;
efi_time_t last_access_time;
efi_time_t modification_time;
__aligned_u64 attribute;
efi_char16_t filename[1];
} efi_file_info_t;
typedef struct efi_file_handle efi_file_handle_t;
struct efi_file_handle {
u64 revision;
efi_status_t (__efiapi *open)(efi_file_handle_t *,
efi_file_handle_t **,
efi_char16_t *, u64, u64);
efi_status_t (__efiapi *close)(efi_file_handle_t *);
void *delete;
efi_status_t (__efiapi *read)(efi_file_handle_t *,
unsigned long *, void *);
void *write;
void *get_position;
void *set_position;
efi_status_t (__efiapi *get_info)(efi_file_handle_t *,
efi_guid_t *, unsigned long *,
void *);
void *set_info;
void *flush;
};
typedef struct efi_file_io_interface efi_file_io_interface_t;
struct efi_file_io_interface {
u64 revision;
int (__efiapi *open_volume)(efi_file_io_interface_t *,
efi_file_handle_t **);
};
#define EFI_FILE_MODE_READ 0x0000000000000001
#define EFI_FILE_MODE_WRITE 0x0000000000000002
#define EFI_FILE_MODE_CREATE 0x8000000000000000
typedef struct { typedef struct {
u32 version; u32 version;
u32 length; u32 length;
@ -865,6 +487,14 @@ typedef struct {
#define EFI_PROPERTIES_TABLE_VERSION 0x00010000 #define EFI_PROPERTIES_TABLE_VERSION 0x00010000
#define EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA 0x1 #define EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA 0x1
typedef struct {
u16 version;
u16 length;
u32 runtime_services_supported;
} efi_rt_properties_table_t;
#define EFI_RT_PROPERTIES_TABLE_VERSION 0x1
#define EFI_INVALID_TABLE_ADDR (~0UL) #define EFI_INVALID_TABLE_ADDR (~0UL)
typedef struct { typedef struct {
@ -896,48 +526,63 @@ typedef struct {
efi_time_t time_of_revocation; efi_time_t time_of_revocation;
} efi_cert_x509_sha256_t; } efi_cert_x509_sha256_t;
extern unsigned long __ro_after_init efi_rng_seed; /* RNG Seed table */
/* /*
* All runtime access to EFI goes through this structure: * All runtime access to EFI goes through this structure:
*/ */
extern struct efi { extern struct efi {
efi_system_table_t *systab; /* EFI system table */ const efi_runtime_services_t *runtime; /* EFI runtime services table */
unsigned int runtime_version; /* Runtime services version */ unsigned int runtime_version; /* Runtime services version */
unsigned long mps; /* MPS table */ unsigned int runtime_supported_mask;
unsigned long acpi; /* ACPI table (IA64 ext 0.71) */
unsigned long acpi20; /* ACPI table (ACPI 2.0) */ unsigned long acpi; /* ACPI table (IA64 ext 0.71) */
unsigned long smbios; /* SMBIOS table (32 bit entry point) */ unsigned long acpi20; /* ACPI table (ACPI 2.0) */
unsigned long smbios3; /* SMBIOS table (64 bit entry point) */ unsigned long smbios; /* SMBIOS table (32 bit entry point) */
unsigned long boot_info; /* boot info table */ unsigned long smbios3; /* SMBIOS table (64 bit entry point) */
unsigned long hcdp; /* HCDP table */ unsigned long esrt; /* ESRT table */
unsigned long uga; /* UGA table */ unsigned long tpm_log; /* TPM2 Event Log table */
unsigned long fw_vendor; /* fw_vendor */ unsigned long tpm_final_log; /* TPM2 Final Events Log table */
unsigned long runtime; /* runtime table */
unsigned long config_table; /* config tables */ efi_get_time_t *get_time;
unsigned long esrt; /* ESRT table */ efi_set_time_t *set_time;
unsigned long properties_table; /* properties table */ efi_get_wakeup_time_t *get_wakeup_time;
unsigned long mem_attr_table; /* memory attributes table */ efi_set_wakeup_time_t *set_wakeup_time;
unsigned long rng_seed; /* UEFI firmware random seed */ efi_get_variable_t *get_variable;
unsigned long tpm_log; /* TPM2 Event Log table */ efi_get_next_variable_t *get_next_variable;
unsigned long tpm_final_log; /* TPM2 Final Events Log table */ efi_set_variable_t *set_variable;
unsigned long mem_reserve; /* Linux EFI memreserve table */ efi_set_variable_t *set_variable_nonblocking;
efi_get_time_t *get_time; efi_query_variable_info_t *query_variable_info;
efi_set_time_t *set_time; efi_query_variable_info_t *query_variable_info_nonblocking;
efi_get_wakeup_time_t *get_wakeup_time; efi_update_capsule_t *update_capsule;
efi_set_wakeup_time_t *set_wakeup_time; efi_query_capsule_caps_t *query_capsule_caps;
efi_get_variable_t *get_variable; efi_get_next_high_mono_count_t *get_next_high_mono_count;
efi_get_next_variable_t *get_next_variable; efi_reset_system_t *reset_system;
efi_set_variable_t *set_variable;
efi_set_variable_t *set_variable_nonblocking; struct efi_memory_map memmap;
efi_query_variable_info_t *query_variable_info; unsigned long flags;
efi_query_variable_info_t *query_variable_info_nonblocking;
efi_update_capsule_t *update_capsule;
efi_query_capsule_caps_t *query_capsule_caps;
efi_get_next_high_mono_count_t *get_next_high_mono_count;
efi_reset_system_t *reset_system;
struct efi_memory_map memmap;
unsigned long flags;
} efi; } efi;
#define EFI_RT_SUPPORTED_GET_TIME 0x0001
#define EFI_RT_SUPPORTED_SET_TIME 0x0002
#define EFI_RT_SUPPORTED_GET_WAKEUP_TIME 0x0004
#define EFI_RT_SUPPORTED_SET_WAKEUP_TIME 0x0008
#define EFI_RT_SUPPORTED_GET_VARIABLE 0x0010
#define EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME 0x0020
#define EFI_RT_SUPPORTED_SET_VARIABLE 0x0040
#define EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP 0x0080
#define EFI_RT_SUPPORTED_CONVERT_POINTER 0x0100
#define EFI_RT_SUPPORTED_GET_NEXT_HIGH_MONOTONIC_COUNT 0x0200
#define EFI_RT_SUPPORTED_RESET_SYSTEM 0x0400
#define EFI_RT_SUPPORTED_UPDATE_CAPSULE 0x0800
#define EFI_RT_SUPPORTED_QUERY_CAPSULE_CAPABILITIES 0x1000
#define EFI_RT_SUPPORTED_QUERY_VARIABLE_INFO 0x2000
#define EFI_RT_SUPPORTED_ALL 0x3fff
#define EFI_RT_SUPPORTED_TIME_SERVICES 0x000f
#define EFI_RT_SUPPORTED_VARIABLE_SERVICES 0x0070
extern struct mm_struct efi_mm; extern struct mm_struct efi_mm;
static inline int static inline int
@ -987,14 +632,18 @@ extern int __init efi_memmap_split_count(efi_memory_desc_t *md,
extern void __init efi_memmap_insert(struct efi_memory_map *old_memmap, extern void __init efi_memmap_insert(struct efi_memory_map *old_memmap,
void *buf, struct efi_mem_range *mem); void *buf, struct efi_mem_range *mem);
extern int efi_config_init(efi_config_table_type_t *arch_tables);
#ifdef CONFIG_EFI_ESRT #ifdef CONFIG_EFI_ESRT
extern void __init efi_esrt_init(void); extern void __init efi_esrt_init(void);
#else #else
static inline void efi_esrt_init(void) { } static inline void efi_esrt_init(void) { }
#endif #endif
extern int efi_config_parse_tables(void *config_tables, int count, int sz, extern int efi_config_parse_tables(const efi_config_table_t *config_tables,
efi_config_table_type_t *arch_tables); int count,
const efi_config_table_type_t *arch_tables);
extern int efi_systab_check_header(const efi_table_hdr_t *systab_hdr,
int min_major_version);
extern void efi_systab_report_header(const efi_table_hdr_t *systab_hdr,
unsigned long fw_vendor);
extern u64 efi_get_iobase (void); extern u64 efi_get_iobase (void);
extern int efi_mem_type(unsigned long phys_addr); extern int efi_mem_type(unsigned long phys_addr);
extern u64 efi_mem_attributes (unsigned long phys_addr); extern u64 efi_mem_attributes (unsigned long phys_addr);
@ -1006,7 +655,7 @@ extern void efi_mem_reserve(phys_addr_t addr, u64 size);
extern int efi_mem_reserve_persistent(phys_addr_t addr, u64 size); extern int efi_mem_reserve_persistent(phys_addr_t addr, u64 size);
extern void efi_initialize_iomem_resources(struct resource *code_resource, extern void efi_initialize_iomem_resources(struct resource *code_resource,
struct resource *data_resource, struct resource *bss_resource); struct resource *data_resource, struct resource *bss_resource);
extern int efi_get_fdt_params(struct efi_fdt_params *params); extern u64 efi_get_fdt_params(struct efi_memory_map_data *data);
extern struct kobject *efi_kobj; extern struct kobject *efi_kobj;
extern int efi_reboot_quirk_mode; extern int efi_reboot_quirk_mode;
@ -1018,6 +667,8 @@ extern void __init efi_fake_memmap(void);
static inline void efi_fake_memmap(void) { } static inline void efi_fake_memmap(void) { }
#endif #endif
extern unsigned long efi_mem_attr_table;
/* /*
* efi_memattr_perm_setter - arch specific callback function passed into * efi_memattr_perm_setter - arch specific callback function passed into
* efi_memattr_apply_permissions() that updates the * efi_memattr_apply_permissions() that updates the
@ -1143,6 +794,11 @@ static inline bool __pure efi_soft_reserve_enabled(void)
return IS_ENABLED(CONFIG_EFI_SOFT_RESERVE) return IS_ENABLED(CONFIG_EFI_SOFT_RESERVE)
&& __efi_soft_reserve_enabled(); && __efi_soft_reserve_enabled();
} }
static inline bool efi_rt_services_supported(unsigned int mask)
{
return (efi.runtime_supported_mask & mask) == mask;
}
#else #else
static inline bool efi_enabled(int feature) static inline bool efi_enabled(int feature)
{ {
@ -1161,6 +817,11 @@ static inline bool efi_soft_reserve_enabled(void)
{ {
return false; return false;
} }
static inline bool efi_rt_services_supported(unsigned int mask)
{
return false;
}
#endif #endif
extern int efi_status_to_err(efi_status_t status); extern int efi_status_to_err(efi_status_t status);
@ -1189,13 +850,6 @@ extern int efi_status_to_err(efi_status_t status);
*/ */
#define EFI_VARIABLE_GUID_LEN UUID_STRING_LEN #define EFI_VARIABLE_GUID_LEN UUID_STRING_LEN
/*
* The type of search to perform when calling boottime->locate_handle
*/
#define EFI_LOCATE_ALL_HANDLES 0
#define EFI_LOCATE_BY_REGISTER_NOTIFY 1
#define EFI_LOCATE_BY_PROTOCOL 2
/* /*
* EFI Device Path information * EFI Device Path information
*/ */
@ -1235,30 +889,40 @@ extern int efi_status_to_err(efi_status_t status);
#define EFI_DEV_END_ENTIRE 0xFF #define EFI_DEV_END_ENTIRE 0xFF
struct efi_generic_dev_path { struct efi_generic_dev_path {
u8 type; u8 type;
u8 sub_type; u8 sub_type;
u16 length; u16 length;
} __attribute ((packed)); } __packed;
struct efi_acpi_dev_path {
struct efi_generic_dev_path header;
u32 hid;
u32 uid;
} __packed;
struct efi_pci_dev_path {
struct efi_generic_dev_path header;
u8 fn;
u8 dev;
} __packed;
struct efi_vendor_dev_path {
struct efi_generic_dev_path header;
efi_guid_t vendorguid;
u8 vendordata[];
} __packed;
struct efi_dev_path { struct efi_dev_path {
u8 type; /* can be replaced with unnamed */
u8 sub_type; /* struct efi_generic_dev_path; */
u16 length; /* once we've moved to -std=c11 */
union { union {
struct { struct efi_generic_dev_path header;
u32 hid; struct efi_acpi_dev_path acpi;
u32 uid; struct efi_pci_dev_path pci;
} acpi; struct efi_vendor_dev_path vendor;
struct {
u8 fn;
u8 dev;
} pci;
}; };
} __attribute ((packed)); } __packed;
#if IS_ENABLED(CONFIG_EFI_DEV_PATH_PARSER) struct device *efi_get_device_by_path(const struct efi_dev_path **node,
struct device *efi_get_device_by_path(struct efi_dev_path **node, size_t *len); size_t *len);
#endif
static inline void memrange_efi_to_native(u64 *addr, u64 *npages) static inline void memrange_efi_to_native(u64 *addr, u64 *npages)
{ {
@ -1313,80 +977,6 @@ struct efivar_entry {
bool deleting; bool deleting;
}; };
union efi_simple_text_output_protocol {
struct {
void *reset;
efi_status_t (__efiapi *output_string)(efi_simple_text_output_protocol_t *,
efi_char16_t *);
void *test_string;
};
struct {
u32 reset;
u32 output_string;
u32 test_string;
} mixed_mode;
};
#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
typedef struct {
u32 red_mask;
u32 green_mask;
u32 blue_mask;
u32 reserved_mask;
} efi_pixel_bitmask_t;
typedef struct {
u32 version;
u32 horizontal_resolution;
u32 vertical_resolution;
int pixel_format;
efi_pixel_bitmask_t pixel_information;
u32 pixels_per_scan_line;
} efi_graphics_output_mode_info_t;
typedef union efi_graphics_output_protocol_mode efi_graphics_output_protocol_mode_t;
union efi_graphics_output_protocol_mode {
struct {
u32 max_mode;
u32 mode;
efi_graphics_output_mode_info_t *info;
unsigned long size_of_info;
efi_physical_addr_t frame_buffer_base;
unsigned long frame_buffer_size;
};
struct {
u32 max_mode;
u32 mode;
u32 info;
u32 size_of_info;
u64 frame_buffer_base;
u32 frame_buffer_size;
} mixed_mode;
};
typedef union efi_graphics_output_protocol efi_graphics_output_protocol_t;
union efi_graphics_output_protocol {
struct {
void *query_mode;
void *set_mode;
void *blt;
efi_graphics_output_protocol_mode_t *mode;
};
struct {
u32 query_mode;
u32 set_mode;
u32 blt;
u32 mode;
} mixed_mode;
};
extern struct list_head efivar_sysfs_list; extern struct list_head efivar_sysfs_list;
static inline void static inline void
@ -1484,52 +1074,6 @@ static inline int efi_runtime_map_copy(void *buf, size_t bufsz)
#endif #endif
/* prototypes shared between arch specific and generic stub code */
void efi_printk(char *str);
void efi_free(unsigned long size, unsigned long addr);
char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len);
efi_status_t efi_get_memory_map(struct efi_boot_memmap *map);
efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align,
unsigned long *addr, unsigned long min);
static inline
efi_status_t efi_low_alloc(unsigned long size, unsigned long align,
unsigned long *addr)
{
/*
* Don't allocate at 0x0. It will confuse code that
* checks pointers against NULL. Skip the first 8
* bytes so we start at a nice even number.
*/
return efi_low_alloc_above(size, align, addr, 0x8);
}
efi_status_t efi_high_alloc(unsigned long size, unsigned long align,
unsigned long *addr, unsigned long max);
efi_status_t efi_relocate_kernel(unsigned long *image_addr,
unsigned long image_size,
unsigned long alloc_size,
unsigned long preferred_addr,
unsigned long alignment,
unsigned long min_addr);
efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
char *cmd_line, char *option_string,
unsigned long max_addr,
unsigned long *load_addr,
unsigned long *load_size);
efi_status_t efi_parse_options(char const *cmdline);
efi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto,
unsigned long size);
#ifdef CONFIG_EFI #ifdef CONFIG_EFI
extern bool efi_runtime_disabled(void); extern bool efi_runtime_disabled(void);
#else #else
@ -1613,15 +1157,6 @@ void efi_retrieve_tpm2_eventlog(void);
arch_efi_call_virt_teardown(); \ arch_efi_call_virt_teardown(); \
}) })
typedef efi_status_t (*efi_exit_boot_map_processing)(
struct efi_boot_memmap *map,
void *priv);
efi_status_t efi_exit_boot_services(void *handle,
struct efi_boot_memmap *map,
void *priv,
efi_exit_boot_map_processing priv_func);
#define EFI_RANDOM_SEED_SIZE 64U #define EFI_RANDOM_SEED_SIZE 64U
struct linux_efi_random_seed { struct linux_efi_random_seed {
@ -1708,6 +1243,4 @@ struct linux_efi_memreserve {
#define EFI_MEMRESERVE_COUNT(size) (((size) - sizeof(struct linux_efi_memreserve)) \ #define EFI_MEMRESERVE_COUNT(size) (((size) - sizeof(struct linux_efi_memreserve)) \
/ sizeof(((struct linux_efi_memreserve *)0)->entry[0])) / sizeof(((struct linux_efi_memreserve *)0)->entry[0]))
void efi_pci_disable_bridge_busmaster(void);
#endif /* _LINUX_EFI_H */ #endif /* _LINUX_EFI_H */

View File

@ -10,6 +10,27 @@
#include <linux/types.h> #include <linux/types.h>
/*
* Linux EFI stub v1.0 adds the following functionality:
* - Loading initrd from the LINUX_EFI_INITRD_MEDIA_GUID device path,
* - Loading/starting the kernel from firmware that targets a different
* machine type, via the entrypoint exposed in the .compat PE/COFF section.
*
* The recommended way of loading and starting v1.0 or later kernels is to use
* the LoadImage() and StartImage() EFI boot services, and expose the initrd
* via the LINUX_EFI_INITRD_MEDIA_GUID device path.
*
* Versions older than v1.0 support initrd loading via the image load options
* (using initrd=, limited to the volume from which the kernel itself was
* loaded), or via arch specific means (bootparams, DT, etc).
*
* On x86, LoadImage() and StartImage() can be omitted if the EFI handover
* protocol is implemented, which can be inferred from the version,
* handover_offset and xloadflags fields in the bootparams structure.
*/
#define LINUX_EFISTUB_MAJOR_VERSION 0x1
#define LINUX_EFISTUB_MINOR_VERSION 0x0
#define MZ_MAGIC 0x5a4d /* "MZ" */ #define MZ_MAGIC 0x5a4d /* "MZ" */
#define PE_MAGIC 0x00004550 /* "PE\0\0" */ #define PE_MAGIC 0x00004550 /* "PE\0\0" */

View File

@ -79,7 +79,7 @@ static int __init load_uefi_certs(void)
efi_status_t status; efi_status_t status;
int rc = 0; int rc = 0;
if (!efi.get_variable) if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
return false; return false;
/* Get db, MokListRT, and dbx. They might not exist, so it isn't /* Get db, MokListRT, and dbx. They might not exist, so it isn't