The EFI changes for this cycle are:
- preliminary changes for RISC-V - Add support for setting the resolution on the EFI framebuffer - Simplify kernel image loading for arm64 - Move .bss into .data via the linker script instead of relying on symbol annotations. - Get rid of __pure getters to access global variables - Clean up the config table matching arrays - Rename pr_efi/pr_efi_err to efi_info/efi_err, and use them consistently - Simplify and unify initrd loading - Parse the builtin command line on x86 (if provided) - Implement printk() support, including support for wide character strings - Simplify GDT handling in early mixed mode thunking code - Some other minor fixes and cleanups Signed-off-by: Ingo Molnar <mingo@kernel.org> -----BEGIN PGP SIGNATURE----- iQJFBAABCgAvFiEEBpT5eoXrXCwVQwEKEnMQ0APhK1gFAl7VANERHG1pbmdvQGtl cm5lbC5vcmcACgkQEnMQ0APhK1iLnhAApADFVx2r/PmBTaLkxTILnyC0zg03kWne Fs6K/npgK68M/Qz4OlXqVhirCHTVMDO4T/4hfckSe0HtzLiGtPeKwea7+ATpdeff iH0k1xCOO9YoUAGKLpOwNPIzR3F2EEJy0vENF/v6KFODuBNJE8Xuq6GAFs9IKoxz zUxFJw/QlKssr4GcxpgW5ODb2rwiP4znyDj/x6/oy81H+RPk+TuDrF/kmHmTQJVN MLZsx8oXQUmgZabDd8xOzQh41KWy8CpF3XbrOA+zGiT8oiRjwTJ49Mo1p53Wm0Ba xSxxXrPvJAT8OrZaeDCdppn0p0OLAl4fuTkAgURZKwAHJLgiERY0N0EFGoDYriVZ qQuBTVuVa3Njg7IKdMyXjyKqX7xmEP+6Mck9j4bg8Q/ss4T4UzpkOxe7txCc/Bqw lf3rzx8IvXxh5ep5H+rqcWfjfygdZrv6hJ0xVkwt1C43pVHWMXlrE+IJqvvQC2q4 KyEkNdGdFFeWiGVo829kuOIVXNFapcSe6G+Q9aCZSLa7LCi+bSNZmi8HzPhiQEr0 t/Z04DUDYgJ3mUfZuKJ7i1FHbuYo7t1iDwx9txeIM2u1S8E68UmgwLHRg2xLjtyu Rkib57mTTi6pO8cSJQVNaSh5F3h870nhrK62kfCgn8c8B5v/C9q56Q0B3hlUIvQ4 R0T8188LdYk= =XG4E -----END PGP SIGNATURE----- Merge tag 'efi-core-2020-06-01' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip Pull EFI updates from Ingo Molnar: "The EFI changes for this cycle are: - preliminary changes for RISC-V - Add support for setting the resolution on the EFI framebuffer - Simplify kernel image loading for arm64 - Move .bss into .data via the linker script instead of relying on symbol annotations. - Get rid of __pure getters to access global variables - Clean up the config table matching arrays - Rename pr_efi/pr_efi_err to efi_info/efi_err, and use them consistently - Simplify and unify initrd loading - Parse the builtin command line on x86 (if provided) - Implement printk() support, including support for wide character strings - Simplify GDT handling in early mixed mode thunking code - Some other minor fixes and cleanups" * tag 'efi-core-2020-06-01' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (79 commits) efi/x86: Don't blow away existing initrd efi/x86: Drop the special GDT for the EFI thunk efi/libstub: Add missing prototype for PE/COFF entry point efi/efivars: Add missing kobject_put() in sysfs entry creation error path efi/libstub: Use pool allocation for the command line efi/libstub: Don't parse overlong command lines efi/libstub: Use snprintf with %ls to convert the command line efi/libstub: Get the exact UTF-8 length efi/libstub: Use %ls for filename efi/libstub: Add UTF-8 decoding to efi_puts efi/printf: Add support for wchar_t (UTF-16) efi/gop: Add an option to list out the available GOP modes efi/libstub: Add definitions for console input and events efi/libstub: Implement printk-style logging efi/printf: Turn vsprintf into vsnprintf efi/printf: Abort on invalid format efi/printf: Refactor code to consolidate padding and output efi/printf: Handle null string input efi/printf: Factor out integer argument retrieval efi/printf: Factor out width/precision parsing ...
This commit is contained in:
commit
58ff3b7604
@ -2,8 +2,10 @@
|
||||
What is efifb?
|
||||
==============
|
||||
|
||||
This is a generic EFI platform driver for Intel based Apple computers.
|
||||
efifb is only for EFI booted Intel Macs.
|
||||
This is a generic EFI platform driver for systems with UEFI firmware. The
|
||||
system must be booted via the EFI stub for this to be usable. efifb supports
|
||||
both firmware with Graphics Output Protocol (GOP) displays as well as older
|
||||
systems with only Universal Graphics Adapter (UGA) displays.
|
||||
|
||||
Supported Hardware
|
||||
==================
|
||||
@ -12,11 +14,14 @@ Supported Hardware
|
||||
- Macbook
|
||||
- Macbook Pro 15"/17"
|
||||
- MacMini
|
||||
- ARM/ARM64/X86 systems with UEFI firmware
|
||||
|
||||
How to use it?
|
||||
==============
|
||||
|
||||
efifb does not have any kind of autodetection of your machine.
|
||||
For UGA displays, efifb does not have any kind of autodetection of your
|
||||
machine.
|
||||
|
||||
You have to add the following kernel parameters in your elilo.conf::
|
||||
|
||||
Macbook :
|
||||
@ -28,6 +33,9 @@ You have to add the following kernel parameters in your elilo.conf::
|
||||
Macbook Pro 17", iMac 20" :
|
||||
video=efifb:i20
|
||||
|
||||
For GOP displays, efifb can autodetect the display's resolution and framebuffer
|
||||
address, so these should work out of the box without any special parameters.
|
||||
|
||||
Accepted options:
|
||||
|
||||
======= ===========================================================
|
||||
@ -36,4 +44,28 @@ nowc Don't map the framebuffer write combined. This can be used
|
||||
when large amounts of console data are written.
|
||||
======= ===========================================================
|
||||
|
||||
Options for GOP displays:
|
||||
|
||||
mode=n
|
||||
The EFI stub will set the mode of the display to mode number n if
|
||||
possible.
|
||||
|
||||
<xres>x<yres>[-(rgb|bgr|<bpp>)]
|
||||
The EFI stub will search for a display mode that matches the specified
|
||||
horizontal and vertical resolution, and optionally bit depth, and set
|
||||
the mode of the display to it if one is found. The bit depth can either
|
||||
"rgb" or "bgr" to match specifically those pixel formats, or a number
|
||||
for a mode with matching bits per pixel.
|
||||
|
||||
auto
|
||||
The EFI stub will choose the mode with the highest resolution (product
|
||||
of horizontal and vertical resolution). If there are multiple modes
|
||||
with the highest resolution, it will choose one with the highest color
|
||||
depth.
|
||||
|
||||
list
|
||||
The EFI stub will list out all the display modes that are available. A
|
||||
specific mode can then be chosen using one of the above options for the
|
||||
next boot.
|
||||
|
||||
Edgar Hucek <gimli@dark-green.com>
|
||||
|
@ -1955,7 +1955,7 @@ config EFI
|
||||
select UCS2_STRING
|
||||
select EFI_PARAMS_FROM_FDT
|
||||
select EFI_STUB
|
||||
select EFI_ARMSTUB
|
||||
select EFI_GENERIC_STUB
|
||||
select EFI_RUNTIME_WRAPPERS
|
||||
---help---
|
||||
This option provides support for runtime services provided
|
||||
|
@ -60,7 +60,7 @@ optional_header:
|
||||
.long __pecoff_code_size @ SizeOfCode
|
||||
.long __pecoff_data_size @ SizeOfInitializedData
|
||||
.long 0 @ SizeOfUninitializedData
|
||||
.long efi_entry - start @ AddressOfEntryPoint
|
||||
.long efi_pe_entry - start @ AddressOfEntryPoint
|
||||
.long start_offset @ BaseOfCode
|
||||
.long __pecoff_data_start - start @ BaseOfData
|
||||
|
||||
|
@ -78,7 +78,7 @@ SECTIONS
|
||||
* The EFI stub always executes from RAM, and runs strictly before the
|
||||
* decompressor, so we can make an exception for its r/w data, and keep it
|
||||
*/
|
||||
*(.data.efistub)
|
||||
*(.data.efistub .bss.efistub)
|
||||
__pecoff_data_end = .;
|
||||
|
||||
/*
|
||||
|
@ -50,14 +50,6 @@ void efi_virtmap_unload(void);
|
||||
|
||||
/* arch specific definitions used by the stub code */
|
||||
|
||||
#define efi_bs_call(func, ...) efi_system_table()->boottime->func(__VA_ARGS__)
|
||||
#define efi_rt_call(func, ...) efi_system_table()->runtime->func(__VA_ARGS__)
|
||||
#define efi_is_native() (true)
|
||||
|
||||
#define efi_table_attr(inst, attr) (inst->attr)
|
||||
|
||||
#define efi_call_proto(inst, func, ...) inst->func(inst, ##__VA_ARGS__)
|
||||
|
||||
struct screen_info *alloc_screen_info(void);
|
||||
void free_screen_info(struct screen_info *si);
|
||||
|
||||
|
@ -1786,7 +1786,7 @@ config EFI
|
||||
select EFI_PARAMS_FROM_FDT
|
||||
select EFI_RUNTIME_WRAPPERS
|
||||
select EFI_STUB
|
||||
select EFI_ARMSTUB
|
||||
select EFI_GENERIC_STUB
|
||||
default y
|
||||
help
|
||||
This option provides support for runtime services provided
|
||||
|
@ -86,14 +86,6 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base,
|
||||
return (image_addr & ~(SZ_1G - 1UL)) + (1UL << (VA_BITS_MIN - 1));
|
||||
}
|
||||
|
||||
#define efi_bs_call(func, ...) efi_system_table()->boottime->func(__VA_ARGS__)
|
||||
#define efi_rt_call(func, ...) efi_system_table()->runtime->func(__VA_ARGS__)
|
||||
#define efi_is_native() (true)
|
||||
|
||||
#define efi_table_attr(inst, attr) (inst->attr)
|
||||
|
||||
#define efi_call_proto(inst, func, ...) inst->func(inst, ##__VA_ARGS__)
|
||||
|
||||
#define alloc_screen_info(x...) &screen_info
|
||||
|
||||
static inline void free_screen_info(struct screen_info *si)
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
SYM_CODE_START(efi_enter_kernel)
|
||||
/*
|
||||
* efi_entry() will have copied the kernel image if necessary and we
|
||||
* efi_pe_entry() will have copied the kernel image if necessary and we
|
||||
* end up here with device tree address in x1 and the kernel entry
|
||||
* point stored in x0. Save those values in registers which are
|
||||
* callee preserved.
|
||||
|
@ -27,7 +27,7 @@ optional_header:
|
||||
.long __initdata_begin - efi_header_end // SizeOfCode
|
||||
.long __pecoff_data_size // SizeOfInitializedData
|
||||
.long 0 // SizeOfUninitializedData
|
||||
.long __efistub_efi_entry - _head // AddressOfEntryPoint
|
||||
.long __efistub_efi_pe_entry - _head // AddressOfEntryPoint
|
||||
.long efi_header_end - _head // BaseOfCode
|
||||
|
||||
extra_header_fields:
|
||||
|
@ -57,12 +57,12 @@ unsigned long hcdp_phys = EFI_INVALID_TABLE_ADDR;
|
||||
unsigned long sal_systab_phys = EFI_INVALID_TABLE_ADDR;
|
||||
|
||||
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},
|
||||
{SAL_SYSTEM_TABLE_GUID, "SALsystab", &sal_systab_phys},
|
||||
{NULL_GUID, NULL, 0},
|
||||
{ESI_TABLE_GUID, &esi_phys, "ESI" },
|
||||
{HCDP_TABLE_GUID, &hcdp_phys, "HCDP" },
|
||||
{MPS_TABLE_GUID, &mps_phys, "MPS" },
|
||||
{PROCESSOR_ABSTRACTION_LAYER_OVERWRITE_GUID, &palo_phys, "PALO" },
|
||||
{SAL_SYSTEM_TABLE_GUID, &sal_systab_phys, "SALsystab" },
|
||||
{},
|
||||
};
|
||||
|
||||
extern efi_status_t efi_call_phys (void *, ...);
|
||||
|
@ -28,8 +28,6 @@ SYM_FUNC_START(__efi64_thunk)
|
||||
push %rbx
|
||||
|
||||
leaq 1f(%rip), %rbp
|
||||
leaq efi_gdt64(%rip), %rbx
|
||||
movl %ebx, 2(%rbx) /* Fixup the gdt base address */
|
||||
|
||||
movl %ds, %eax
|
||||
push %rax
|
||||
@ -48,7 +46,8 @@ SYM_FUNC_START(__efi64_thunk)
|
||||
movl %r8d, 0xc(%rsp)
|
||||
movl %r9d, 0x10(%rsp)
|
||||
|
||||
sgdt 0x14(%rsp)
|
||||
leaq 0x14(%rsp), %rbx
|
||||
sgdt (%rbx)
|
||||
|
||||
/*
|
||||
* Switch to gdt with 32-bit segments. This is the firmware GDT
|
||||
@ -68,8 +67,7 @@ SYM_FUNC_START(__efi64_thunk)
|
||||
pushq %rax
|
||||
lretq
|
||||
|
||||
1: lgdt 0x14(%rsp)
|
||||
addq $32, %rsp
|
||||
1: addq $32, %rsp
|
||||
movq %rdi, %rax
|
||||
|
||||
pop %rbx
|
||||
@ -175,14 +173,3 @@ 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)
|
||||
.word efi_gdt64_end - efi_gdt64
|
||||
.long 0 /* Filled out by user */
|
||||
.word 0
|
||||
.quad 0x0000000000000000 /* NULL descriptor */
|
||||
.quad 0x00af9a000000ffff /* __KERNEL_CS */
|
||||
.quad 0x00cf92000000ffff /* __KERNEL_DS */
|
||||
.quad 0x0080890000000000 /* TS descriptor */
|
||||
.quad 0x0000000000000000 /* TS continued */
|
||||
SYM_DATA_END_LABEL(efi_gdt64, SYM_L_LOCAL, efi_gdt64_end)
|
||||
|
@ -52,6 +52,7 @@ SECTIONS
|
||||
_data = . ;
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
*(.bss.efistub)
|
||||
_edata = . ;
|
||||
}
|
||||
. = ALIGN(L1_CACHE_BYTES);
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <asm/nospec-branch.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <linux/build_bug.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
extern unsigned long efi_fw_vendor, efi_config_table;
|
||||
|
||||
@ -225,14 +226,21 @@ efi_status_t efi_set_virtual_address_map(unsigned long memory_map_size,
|
||||
|
||||
/* arch specific definitions used by the stub code */
|
||||
|
||||
__attribute_const__ bool efi_is_64bit(void);
|
||||
#ifdef CONFIG_EFI_MIXED
|
||||
|
||||
#define ARCH_HAS_EFISTUB_WRAPPERS
|
||||
|
||||
static inline bool efi_is_64bit(void)
|
||||
{
|
||||
extern const bool efi_is64;
|
||||
|
||||
return efi_is64;
|
||||
}
|
||||
|
||||
static inline bool efi_is_native(void)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_X86_64))
|
||||
return true;
|
||||
if (!IS_ENABLED(CONFIG_EFI_MIXED))
|
||||
return true;
|
||||
return efi_is_64bit();
|
||||
}
|
||||
|
||||
@ -286,6 +294,15 @@ static inline u32 efi64_convert_status(efi_status_t status)
|
||||
#define __efi64_argmap_allocate_pool(type, size, buffer) \
|
||||
((type), (size), efi64_zero_upper(buffer))
|
||||
|
||||
#define __efi64_argmap_create_event(type, tpl, f, c, event) \
|
||||
((type), (tpl), (f), (c), efi64_zero_upper(event))
|
||||
|
||||
#define __efi64_argmap_set_timer(event, type, time) \
|
||||
((event), (type), lower_32_bits(time), upper_32_bits(time))
|
||||
|
||||
#define __efi64_argmap_wait_for_event(num, event, index) \
|
||||
((num), (event), efi64_zero_upper(index))
|
||||
|
||||
#define __efi64_argmap_handle_protocol(handle, protocol, interface) \
|
||||
((handle), (protocol), efi64_zero_upper(interface))
|
||||
|
||||
@ -307,6 +324,10 @@ static inline u32 efi64_convert_status(efi_status_t status)
|
||||
#define __efi64_argmap_load_file(protocol, path, policy, bufsize, buf) \
|
||||
((protocol), (path), (policy), efi64_zero_upper(bufsize), (buf))
|
||||
|
||||
/* Graphics Output Protocol */
|
||||
#define __efi64_argmap_query_mode(gop, mode, size, info) \
|
||||
((gop), (mode), efi64_zero_upper(size), efi64_zero_upper(info))
|
||||
|
||||
/*
|
||||
* The macros below handle the plumbing for the argument mapping. To add a
|
||||
* mapping for a specific EFI method, simply define a macro
|
||||
@ -335,15 +356,26 @@ static inline u32 efi64_convert_status(efi_status_t status)
|
||||
|
||||
#define efi_bs_call(func, ...) \
|
||||
(efi_is_native() \
|
||||
? efi_system_table()->boottime->func(__VA_ARGS__) \
|
||||
: __efi64_thunk_map(efi_table_attr(efi_system_table(), \
|
||||
boottime), func, __VA_ARGS__))
|
||||
? efi_system_table->boottime->func(__VA_ARGS__) \
|
||||
: __efi64_thunk_map(efi_table_attr(efi_system_table, \
|
||||
boottime), \
|
||||
func, __VA_ARGS__))
|
||||
|
||||
#define efi_rt_call(func, ...) \
|
||||
(efi_is_native() \
|
||||
? efi_system_table()->runtime->func(__VA_ARGS__) \
|
||||
: __efi64_thunk_map(efi_table_attr(efi_system_table(), \
|
||||
runtime), func, __VA_ARGS__))
|
||||
? efi_system_table->runtime->func(__VA_ARGS__) \
|
||||
: __efi64_thunk_map(efi_table_attr(efi_system_table, \
|
||||
runtime), \
|
||||
func, __VA_ARGS__))
|
||||
|
||||
#else /* CONFIG_EFI_MIXED */
|
||||
|
||||
static inline bool efi_is_64bit(void)
|
||||
{
|
||||
return IS_ENABLED(CONFIG_X86_64);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_EFI_MIXED */
|
||||
|
||||
extern bool efi_reboot_required(void);
|
||||
extern bool efi_is_table_address(unsigned long phys_addr);
|
||||
|
@ -62,12 +62,12 @@ static unsigned long efi_runtime, efi_nr_tables;
|
||||
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},
|
||||
{EFI_PROPERTIES_TABLE_GUID, &prop_phys, "PROP" },
|
||||
{UGA_IO_PROTOCOL_GUID, &uga_phys, "UGA" },
|
||||
#ifdef CONFIG_X86_UV
|
||||
{UV_SYSTEM_TABLE_GUID, "UVsystab", &uv_systab_phys},
|
||||
{UV_SYSTEM_TABLE_GUID, &uv_systab_phys, "UVsystab" },
|
||||
#endif
|
||||
{NULL_GUID, NULL, NULL},
|
||||
{},
|
||||
};
|
||||
|
||||
static const unsigned long * const efi_tables[] = {
|
||||
|
@ -29,7 +29,7 @@ static efi_system_table_t efi_systab_xen __initdata = {
|
||||
.fw_vendor = EFI_INVALID_TABLE_ADDR, /* Initialized later. */
|
||||
.fw_revision = 0, /* Initialized later. */
|
||||
.con_in_handle = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */
|
||||
.con_in = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */
|
||||
.con_in = NULL, /* Not used under Xen. */
|
||||
.con_out_handle = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */
|
||||
.con_out = NULL, /* Not used under Xen. */
|
||||
.stderr_handle = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */
|
||||
|
@ -106,12 +106,12 @@ config EFI_PARAMS_FROM_FDT
|
||||
config EFI_RUNTIME_WRAPPERS
|
||||
bool
|
||||
|
||||
config EFI_ARMSTUB
|
||||
config EFI_GENERIC_STUB
|
||||
bool
|
||||
|
||||
config EFI_ARMSTUB_DTB_LOADER
|
||||
bool "Enable the DTB loader"
|
||||
depends on EFI_ARMSTUB
|
||||
depends on EFI_GENERIC_STUB
|
||||
default y
|
||||
help
|
||||
Select this config option to add support for the dtb= command
|
||||
@ -124,6 +124,17 @@ config EFI_ARMSTUB_DTB_LOADER
|
||||
functionality for bootloaders that do not have such support
|
||||
this option is necessary.
|
||||
|
||||
config EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER
|
||||
bool "Enable the command line initrd loader" if !X86
|
||||
depends on EFI_STUB && (EFI_GENERIC_STUB || X86)
|
||||
default y
|
||||
help
|
||||
Select this config option to add support for the initrd= command
|
||||
line parameter, allowing an initrd that resides on the same volume
|
||||
as the kernel image to be loaded into memory.
|
||||
|
||||
This method is deprecated.
|
||||
|
||||
config EFI_BOOTLOADER_CONTROL
|
||||
tristate "EFI Bootloader Control"
|
||||
depends on EFI_VARS
|
||||
|
@ -54,8 +54,8 @@ static phys_addr_t __init efi_to_phys(unsigned long addr)
|
||||
static __initdata unsigned long screen_info_table = EFI_INVALID_TABLE_ADDR;
|
||||
|
||||
static const efi_config_table_type_t arch_tables[] __initconst = {
|
||||
{LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, NULL, &screen_info_table},
|
||||
{NULL_GUID, NULL, NULL}
|
||||
{LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, &screen_info_table},
|
||||
{}
|
||||
};
|
||||
|
||||
static void __init init_screen_info(void)
|
||||
|
@ -499,21 +499,21 @@ void __init efi_mem_reserve(phys_addr_t addr, u64 size)
|
||||
}
|
||||
|
||||
static const efi_config_table_type_t common_tables[] __initconst = {
|
||||
{ACPI_20_TABLE_GUID, "ACPI 2.0", &efi.acpi20},
|
||||
{ACPI_TABLE_GUID, "ACPI", &efi.acpi},
|
||||
{SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios},
|
||||
{SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3},
|
||||
{EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt},
|
||||
{EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi_mem_attr_table},
|
||||
{LINUX_EFI_RANDOM_SEED_TABLE_GUID, "RNG", &efi_rng_seed},
|
||||
{LINUX_EFI_TPM_EVENT_LOG_GUID, "TPMEventLog", &efi.tpm_log},
|
||||
{LINUX_EFI_TPM_FINAL_LOG_GUID, "TPMFinalLog", &efi.tpm_final_log},
|
||||
{LINUX_EFI_MEMRESERVE_TABLE_GUID, "MEMRESERVE", &mem_reserve},
|
||||
{EFI_RT_PROPERTIES_TABLE_GUID, "RTPROP", &rt_prop},
|
||||
{ACPI_20_TABLE_GUID, &efi.acpi20, "ACPI 2.0" },
|
||||
{ACPI_TABLE_GUID, &efi.acpi, "ACPI" },
|
||||
{SMBIOS_TABLE_GUID, &efi.smbios, "SMBIOS" },
|
||||
{SMBIOS3_TABLE_GUID, &efi.smbios3, "SMBIOS 3.0" },
|
||||
{EFI_SYSTEM_RESOURCE_TABLE_GUID, &efi.esrt, "ESRT" },
|
||||
{EFI_MEMORY_ATTRIBUTES_TABLE_GUID, &efi_mem_attr_table, "MEMATTR" },
|
||||
{LINUX_EFI_RANDOM_SEED_TABLE_GUID, &efi_rng_seed, "RNG" },
|
||||
{LINUX_EFI_TPM_EVENT_LOG_GUID, &efi.tpm_log, "TPMEventLog" },
|
||||
{LINUX_EFI_TPM_FINAL_LOG_GUID, &efi.tpm_final_log, "TPMFinalLog" },
|
||||
{LINUX_EFI_MEMRESERVE_TABLE_GUID, &mem_reserve, "MEMRESERVE" },
|
||||
{EFI_RT_PROPERTIES_TABLE_GUID, &rt_prop, "RTPROP" },
|
||||
#ifdef CONFIG_EFI_RCI2_TABLE
|
||||
{DELLEMC_EFI_RCI2_TABLE_GUID, NULL, &rci2_table_phys},
|
||||
{DELLEMC_EFI_RCI2_TABLE_GUID, &rci2_table_phys },
|
||||
#endif
|
||||
{NULL_GUID, NULL, NULL},
|
||||
{},
|
||||
};
|
||||
|
||||
static __init int match_config_table(const efi_guid_t *guid,
|
||||
@ -522,15 +522,13 @@ static __init int match_config_table(const efi_guid_t *guid,
|
||||
{
|
||||
int i;
|
||||
|
||||
if (table_types) {
|
||||
for (i = 0; efi_guidcmp(table_types[i].guid, NULL_GUID); i++) {
|
||||
if (!efi_guidcmp(*guid, table_types[i].guid)) {
|
||||
*(table_types[i].ptr) = table;
|
||||
if (table_types[i].name)
|
||||
pr_cont(" %s=0x%lx ",
|
||||
table_types[i].name, table);
|
||||
return 1;
|
||||
}
|
||||
for (i = 0; efi_guidcmp(table_types[i].guid, NULL_GUID); i++) {
|
||||
if (!efi_guidcmp(*guid, table_types[i].guid)) {
|
||||
*(table_types[i].ptr) = table;
|
||||
if (table_types[i].name[0])
|
||||
pr_cont("%s=0x%lx ",
|
||||
table_types[i].name, table);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -567,7 +565,7 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables,
|
||||
table = tbl32[i].table;
|
||||
}
|
||||
|
||||
if (!match_config_table(guid, table, common_tables))
|
||||
if (!match_config_table(guid, table, common_tables) && arch_tables)
|
||||
match_config_table(guid, table, arch_tables);
|
||||
}
|
||||
pr_cont("\n");
|
||||
|
@ -522,8 +522,10 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var)
|
||||
ret = kobject_init_and_add(&new_var->kobj, &efivar_ktype,
|
||||
NULL, "%s", short_name);
|
||||
kfree(short_name);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
kobject_put(&new_var->kobj);
|
||||
return ret;
|
||||
}
|
||||
|
||||
kobject_uevent(&new_var->kobj, KOBJ_ADD);
|
||||
if (efivar_entry_add(new_var, &efivar_sysfs_list)) {
|
||||
|
@ -7,7 +7,7 @@
|
||||
#
|
||||
cflags-$(CONFIG_X86_32) := -march=i386
|
||||
cflags-$(CONFIG_X86_64) := -mcmodel=small
|
||||
cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ -O2 \
|
||||
cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ \
|
||||
-fPIC -fno-strict-aliasing -mno-red-zone \
|
||||
-mno-mmx -mno-sse -fshort-wchar \
|
||||
-Wno-pointer-sign \
|
||||
@ -23,13 +23,14 @@ cflags-$(CONFIG_ARM) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \
|
||||
-fno-builtin -fpic \
|
||||
$(call cc-option,-mno-single-pic-base)
|
||||
|
||||
cflags-$(CONFIG_EFI_ARMSTUB) += -I$(srctree)/scripts/dtc/libfdt
|
||||
cflags-$(CONFIG_EFI_GENERIC_STUB) += -I$(srctree)/scripts/dtc/libfdt
|
||||
|
||||
KBUILD_CFLAGS := $(cflags-y) -DDISABLE_BRANCH_PROFILING \
|
||||
KBUILD_CFLAGS := $(cflags-y) -Os -DDISABLE_BRANCH_PROFILING \
|
||||
-include $(srctree)/drivers/firmware/efi/libstub/hidden.h \
|
||||
-D__NO_FORTIFY \
|
||||
$(call cc-option,-ffreestanding) \
|
||||
$(call cc-option,-fno-stack-protector) \
|
||||
$(call cc-option,-fno-addrsig) \
|
||||
-D__DISABLE_EXPORTS
|
||||
|
||||
GCOV_PROFILE := n
|
||||
@ -42,16 +43,17 @@ KCOV_INSTRUMENT := n
|
||||
|
||||
lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \
|
||||
file.o mem.o random.o randomalloc.o pci.o \
|
||||
skip_spaces.o lib-cmdline.o lib-ctype.o
|
||||
skip_spaces.o lib-cmdline.o lib-ctype.o \
|
||||
alignedmem.o relocate.o vsprintf.o
|
||||
|
||||
# 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
|
||||
efi-deps-y := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c
|
||||
|
||||
$(obj)/lib-%.o: $(srctree)/lib/%.c FORCE
|
||||
$(call if_changed_rule,cc_o_c)
|
||||
|
||||
lib-$(CONFIG_EFI_ARMSTUB) += arm-stub.o fdt.o string.o \
|
||||
$(patsubst %.c,lib-%.o,$(arm-deps-y))
|
||||
lib-$(CONFIG_EFI_GENERIC_STUB) += efi-stub.o fdt.o string.o \
|
||||
$(patsubst %.c,lib-%.o,$(efi-deps-y))
|
||||
|
||||
lib-$(CONFIG_ARM) += arm32-stub.o
|
||||
lib-$(CONFIG_ARM64) += arm64-stub.o
|
||||
@ -59,6 +61,25 @@ lib-$(CONFIG_X86) += x86-stub.o
|
||||
CFLAGS_arm32-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
|
||||
CFLAGS_arm64-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
|
||||
|
||||
#
|
||||
# For x86, bootloaders like systemd-boot or grub-efi do not zero-initialize the
|
||||
# .bss section, so the .bss section of the EFI stub needs to be included in the
|
||||
# .data section of the compressed kernel to ensure initialization. Rename the
|
||||
# .bss section here so it's easy to pick out in the linker script.
|
||||
#
|
||||
STUBCOPY_FLAGS-$(CONFIG_X86) += --rename-section .bss=.bss.efistub,load,alloc
|
||||
STUBCOPY_RELOC-$(CONFIG_X86_32) := R_386_32
|
||||
STUBCOPY_RELOC-$(CONFIG_X86_64) := R_X86_64_64
|
||||
|
||||
#
|
||||
# ARM discards the .data section because it disallows r/w data in the
|
||||
# decompressor. So move our .data to .data.efistub and .bss to .bss.efistub,
|
||||
# which are preserved explicitly by the decompressor linker script.
|
||||
#
|
||||
STUBCOPY_FLAGS-$(CONFIG_ARM) += --rename-section .data=.data.efistub \
|
||||
--rename-section .bss=.bss.efistub,load,alloc
|
||||
STUBCOPY_RELOC-$(CONFIG_ARM) := R_ARM_ABS
|
||||
|
||||
#
|
||||
# arm64 puts the stub in the kernel proper, which will unnecessarily retain all
|
||||
# code indefinitely unless it is annotated as __init/__initdata/__initconst etc.
|
||||
@ -73,8 +94,8 @@ CFLAGS_arm64-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
|
||||
# a verification pass to see if any absolute relocations exist in any of the
|
||||
# object files.
|
||||
#
|
||||
extra-$(CONFIG_EFI_ARMSTUB) := $(lib-y)
|
||||
lib-$(CONFIG_EFI_ARMSTUB) := $(patsubst %.o,%.stub.o,$(lib-y))
|
||||
extra-y := $(lib-y)
|
||||
lib-y := $(patsubst %.o,%.stub.o,$(lib-y))
|
||||
|
||||
STUBCOPY_FLAGS-$(CONFIG_ARM64) += --prefix-alloc-sections=.init \
|
||||
--prefix-symbols=__efistub_
|
||||
@ -97,11 +118,3 @@ quiet_cmd_stubcopy = STUBCPY $@
|
||||
/bin/false; \
|
||||
fi; \
|
||||
$(OBJCOPY) $(STUBCOPY_FLAGS-y) $< $@
|
||||
|
||||
#
|
||||
# ARM discards the .data section because it disallows r/w data in the
|
||||
# decompressor. So move our .data to .data.efistub, which is preserved
|
||||
# explicitly by the decompressor linker script.
|
||||
#
|
||||
STUBCOPY_FLAGS-$(CONFIG_ARM) += --rename-section .data=.data.efistub
|
||||
STUBCOPY_RELOC-$(CONFIG_ARM) := R_ARM_ABS
|
||||
|
57
drivers/firmware/efi/libstub/alignedmem.c
Normal file
57
drivers/firmware/efi/libstub/alignedmem.c
Normal file
@ -0,0 +1,57 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/efi.h>
|
||||
#include <asm/efi.h>
|
||||
|
||||
#include "efistub.h"
|
||||
|
||||
/**
|
||||
* efi_allocate_pages_aligned() - 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
|
||||
* @align: minimum alignment of the base of the allocation
|
||||
*
|
||||
* Allocate pages as EFI_LOADER_DATA. The allocated pages are aligned according
|
||||
* to @align, which should be >= EFI_ALLOC_ALIGN. The last allocated page will
|
||||
* not exceed the address given by @max.
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
efi_status_t efi_allocate_pages_aligned(unsigned long size, unsigned long *addr,
|
||||
unsigned long max, unsigned long align)
|
||||
{
|
||||
efi_physical_addr_t alloc_addr;
|
||||
efi_status_t status;
|
||||
int slack;
|
||||
|
||||
if (align < EFI_ALLOC_ALIGN)
|
||||
align = EFI_ALLOC_ALIGN;
|
||||
|
||||
alloc_addr = ALIGN_DOWN(max + 1, align) - 1;
|
||||
size = round_up(size, EFI_ALLOC_ALIGN);
|
||||
slack = align / EFI_PAGE_SIZE - 1;
|
||||
|
||||
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, align);
|
||||
|
||||
if (slack > 0) {
|
||||
int l = (alloc_addr % 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;
|
||||
}
|
@ -18,7 +18,7 @@ efi_status_t check_platform_features(void)
|
||||
/* LPAE kernels need compatible hardware */
|
||||
block = cpuid_feature_extract(CPUID_EXT_MMFR0, 0);
|
||||
if (block < 5) {
|
||||
pr_efi_err("This LPAE kernel is not supported by your CPU\n");
|
||||
efi_err("This LPAE kernel is not supported by your CPU\n");
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
return EFI_SUCCESS;
|
||||
@ -120,7 +120,7 @@ static efi_status_t reserve_kernel_base(unsigned long dram_base,
|
||||
*/
|
||||
status = efi_get_memory_map(&map);
|
||||
if (status != EFI_SUCCESS) {
|
||||
pr_efi_err("reserve_kernel_base(): Unable to retrieve memory map.\n");
|
||||
efi_err("reserve_kernel_base(): Unable to retrieve memory map.\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -162,7 +162,7 @@ static efi_status_t reserve_kernel_base(unsigned long dram_base,
|
||||
(end - start) / EFI_PAGE_SIZE,
|
||||
&start);
|
||||
if (status != EFI_SUCCESS) {
|
||||
pr_efi_err("reserve_kernel_base(): alloc failed.\n");
|
||||
efi_err("reserve_kernel_base(): alloc failed.\n");
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
@ -219,7 +219,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
|
||||
|
||||
status = reserve_kernel_base(kernel_base, reserve_addr, reserve_size);
|
||||
if (status != EFI_SUCCESS) {
|
||||
pr_efi_err("Unable to allocate memory for uncompressed kernel.\n");
|
||||
efi_err("Unable to allocate memory for uncompressed kernel.\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -232,7 +232,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
|
||||
status = efi_relocate_kernel(image_addr, *image_size, *image_size,
|
||||
kernel_base + MAX_UNCOMP_KERNEL_SIZE, 0, 0);
|
||||
if (status != EFI_SUCCESS) {
|
||||
pr_efi_err("Failed to relocate kernel.\n");
|
||||
efi_err("Failed to relocate kernel.\n");
|
||||
efi_free(*reserve_size, *reserve_addr);
|
||||
*reserve_size = 0;
|
||||
return status;
|
||||
@ -244,7 +244,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
|
||||
* address at which the zImage is loaded.
|
||||
*/
|
||||
if (*image_addr + *image_size > dram_base + ZIMAGE_OFFSET_LIMIT) {
|
||||
pr_efi_err("Failed to relocate kernel, no low memory available.\n");
|
||||
efi_err("Failed to relocate kernel, no low memory available.\n");
|
||||
efi_free(*reserve_size, *reserve_addr);
|
||||
*reserve_size = 0;
|
||||
efi_free(*image_size, *image_addr);
|
||||
|
@ -26,14 +26,23 @@ efi_status_t check_platform_features(void)
|
||||
tg = (read_cpuid(ID_AA64MMFR0_EL1) >> ID_AA64MMFR0_TGRAN_SHIFT) & 0xf;
|
||||
if (tg != ID_AA64MMFR0_TGRAN_SUPPORTED) {
|
||||
if (IS_ENABLED(CONFIG_ARM64_64K_PAGES))
|
||||
pr_efi_err("This 64 KB granular kernel is not supported by your CPU\n");
|
||||
efi_err("This 64 KB granular kernel is not supported by your CPU\n");
|
||||
else
|
||||
pr_efi_err("This 16 KB granular kernel is not supported by your CPU\n");
|
||||
efi_err("This 16 KB granular kernel is not supported by your CPU\n");
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Relocatable kernels can fix up the misalignment with respect to
|
||||
* MIN_KIMG_ALIGN, so they only require a minimum alignment of EFI_KIMG_ALIGN
|
||||
* (which accounts for the alignment of statically allocated objects such as
|
||||
* the swapper stack.)
|
||||
*/
|
||||
static const u64 min_kimg_align = IS_ENABLED(CONFIG_RELOCATABLE) ? EFI_KIMG_ALIGN
|
||||
: MIN_KIMG_ALIGN;
|
||||
|
||||
efi_status_t handle_kernel_image(unsigned long *image_addr,
|
||||
unsigned long *image_size,
|
||||
unsigned long *reserve_addr,
|
||||
@ -43,106 +52,63 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
|
||||
{
|
||||
efi_status_t status;
|
||||
unsigned long kernel_size, kernel_memsize = 0;
|
||||
unsigned long preferred_offset;
|
||||
u64 phys_seed = 0;
|
||||
u32 phys_seed = 0;
|
||||
|
||||
if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
|
||||
if (!nokaslr()) {
|
||||
if (!efi_nokaslr) {
|
||||
status = efi_get_random_bytes(sizeof(phys_seed),
|
||||
(u8 *)&phys_seed);
|
||||
if (status == EFI_NOT_FOUND) {
|
||||
pr_efi("EFI_RNG_PROTOCOL unavailable, no randomness supplied\n");
|
||||
efi_info("EFI_RNG_PROTOCOL unavailable, no randomness supplied\n");
|
||||
} else if (status != EFI_SUCCESS) {
|
||||
pr_efi_err("efi_get_random_bytes() failed\n");
|
||||
efi_err("efi_get_random_bytes() failed\n");
|
||||
return status;
|
||||
}
|
||||
} else {
|
||||
pr_efi("KASLR disabled on kernel command line\n");
|
||||
efi_info("KASLR disabled on kernel command line\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The preferred offset of the kernel Image is TEXT_OFFSET bytes beyond
|
||||
* a 2 MB aligned base, which itself may be lower than dram_base, as
|
||||
* long as the resulting offset equals or exceeds it.
|
||||
*/
|
||||
preferred_offset = round_down(dram_base, MIN_KIMG_ALIGN) + TEXT_OFFSET;
|
||||
if (preferred_offset < dram_base)
|
||||
preferred_offset += MIN_KIMG_ALIGN;
|
||||
if (image->image_base != _text)
|
||||
efi_err("FIRMWARE BUG: efi_loaded_image_t::image_base has bogus value\n");
|
||||
|
||||
kernel_size = _edata - _text;
|
||||
kernel_memsize = kernel_size + (_end - _edata);
|
||||
*reserve_size = kernel_memsize + TEXT_OFFSET % min_kimg_align;
|
||||
|
||||
if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && phys_seed != 0) {
|
||||
/*
|
||||
* Produce a displacement in the interval [0, MIN_KIMG_ALIGN)
|
||||
* that doesn't violate this kernel's de-facto alignment
|
||||
* constraints.
|
||||
*/
|
||||
u32 mask = (MIN_KIMG_ALIGN - 1) & ~(EFI_KIMG_ALIGN - 1);
|
||||
u32 offset = (phys_seed >> 32) & mask;
|
||||
|
||||
/*
|
||||
* With CONFIG_RANDOMIZE_TEXT_OFFSET=y, TEXT_OFFSET may not
|
||||
* be a multiple of EFI_KIMG_ALIGN, and we must ensure that
|
||||
* we preserve the misalignment of 'offset' relative to
|
||||
* EFI_KIMG_ALIGN so that statically allocated objects whose
|
||||
* alignment exceeds PAGE_SIZE appear correctly aligned in
|
||||
* memory.
|
||||
*/
|
||||
offset |= TEXT_OFFSET % EFI_KIMG_ALIGN;
|
||||
|
||||
/*
|
||||
* If KASLR is enabled, and we have some randomness available,
|
||||
* locate the kernel at a randomized offset in physical memory.
|
||||
*/
|
||||
*reserve_size = kernel_memsize + offset;
|
||||
status = efi_random_alloc(*reserve_size,
|
||||
MIN_KIMG_ALIGN, reserve_addr,
|
||||
(u32)phys_seed);
|
||||
|
||||
*image_addr = *reserve_addr + offset;
|
||||
status = efi_random_alloc(*reserve_size, min_kimg_align,
|
||||
reserve_addr, phys_seed);
|
||||
} else {
|
||||
/*
|
||||
* Else, try a straight allocation at the preferred offset.
|
||||
* This will work around the issue where, if dram_base == 0x0,
|
||||
* efi_low_alloc() refuses to allocate at 0x0 (to prevent the
|
||||
* address of the allocation to be mistaken for a FAIL return
|
||||
* value or a NULL pointer). It will also ensure that, on
|
||||
* platforms where the [dram_base, dram_base + TEXT_OFFSET)
|
||||
* interval is partially occupied by the firmware (like on APM
|
||||
* Mustang), we can still place the kernel at the address
|
||||
* 'dram_base + TEXT_OFFSET'.
|
||||
*/
|
||||
*image_addr = (unsigned long)_text;
|
||||
if (*image_addr == preferred_offset)
|
||||
return EFI_SUCCESS;
|
||||
|
||||
*image_addr = *reserve_addr = preferred_offset;
|
||||
*reserve_size = round_up(kernel_memsize, EFI_ALLOC_ALIGN);
|
||||
|
||||
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
|
||||
EFI_LOADER_DATA,
|
||||
*reserve_size / EFI_PAGE_SIZE,
|
||||
(efi_physical_addr_t *)reserve_addr);
|
||||
status = EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
*reserve_size = kernel_memsize + TEXT_OFFSET;
|
||||
status = efi_low_alloc(*reserve_size,
|
||||
MIN_KIMG_ALIGN, reserve_addr);
|
||||
if (IS_ALIGNED((u64)_text - TEXT_OFFSET, min_kimg_align)) {
|
||||
/*
|
||||
* Just execute from wherever we were loaded by the
|
||||
* UEFI PE/COFF loader if the alignment is suitable.
|
||||
*/
|
||||
*image_addr = (u64)_text;
|
||||
*reserve_size = 0;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
status = efi_allocate_pages_aligned(*reserve_size, reserve_addr,
|
||||
ULONG_MAX, min_kimg_align);
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
pr_efi_err("Failed to relocate kernel\n");
|
||||
efi_err("Failed to relocate kernel\n");
|
||||
*reserve_size = 0;
|
||||
return status;
|
||||
}
|
||||
*image_addr = *reserve_addr + TEXT_OFFSET;
|
||||
}
|
||||
|
||||
if (image->image_base != _text)
|
||||
pr_efi_err("FIRMWARE BUG: efi_loaded_image_t::image_base has bogus value\n");
|
||||
|
||||
*image_addr = *reserve_addr + TEXT_OFFSET % min_kimg_align;
|
||||
memcpy((void *)*image_addr, _text, kernel_size);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
|
@ -7,60 +7,151 @@
|
||||
* Copyright 2011 Intel Corporation; author Matt Fleming
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/efi.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/printk.h> /* For CONSOLE_LOGLEVEL_* */
|
||||
#include <asm/efi.h>
|
||||
#include <asm/setup.h>
|
||||
|
||||
#include "efistub.h"
|
||||
|
||||
static bool __efistub_global efi_nochunk;
|
||||
static bool __efistub_global efi_nokaslr;
|
||||
static bool __efistub_global efi_noinitrd;
|
||||
static bool __efistub_global efi_quiet;
|
||||
static bool __efistub_global efi_novamap;
|
||||
static bool __efistub_global efi_nosoftreserve;
|
||||
static bool __efistub_global efi_disable_pci_dma =
|
||||
IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA);
|
||||
bool efi_nochunk;
|
||||
bool efi_nokaslr;
|
||||
bool efi_noinitrd;
|
||||
int efi_loglevel = CONSOLE_LOGLEVEL_DEFAULT;
|
||||
bool efi_novamap;
|
||||
|
||||
static bool efi_nosoftreserve;
|
||||
static bool efi_disable_pci_dma = IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA);
|
||||
|
||||
bool __pure nochunk(void)
|
||||
{
|
||||
return efi_nochunk;
|
||||
}
|
||||
bool __pure nokaslr(void)
|
||||
{
|
||||
return efi_nokaslr;
|
||||
}
|
||||
bool __pure noinitrd(void)
|
||||
{
|
||||
return efi_noinitrd;
|
||||
}
|
||||
bool __pure is_quiet(void)
|
||||
{
|
||||
return efi_quiet;
|
||||
}
|
||||
bool __pure novamap(void)
|
||||
{
|
||||
return efi_novamap;
|
||||
}
|
||||
bool __pure __efi_soft_reserve_enabled(void)
|
||||
{
|
||||
return !efi_nosoftreserve;
|
||||
}
|
||||
|
||||
void efi_printk(char *str)
|
||||
void efi_char16_puts(efi_char16_t *str)
|
||||
{
|
||||
char *s8;
|
||||
efi_call_proto(efi_table_attr(efi_system_table, con_out),
|
||||
output_string, str);
|
||||
}
|
||||
|
||||
for (s8 = str; *s8; s8++) {
|
||||
efi_char16_t ch[2] = { 0 };
|
||||
static
|
||||
u32 utf8_to_utf32(const u8 **s8)
|
||||
{
|
||||
u32 c32;
|
||||
u8 c0, cx;
|
||||
size_t clen, i;
|
||||
|
||||
ch[0] = *s8;
|
||||
if (*s8 == '\n') {
|
||||
efi_char16_t nl[2] = { '\r', 0 };
|
||||
efi_char16_printk(nl);
|
||||
}
|
||||
|
||||
efi_char16_printk(ch);
|
||||
c0 = cx = *(*s8)++;
|
||||
/*
|
||||
* The position of the most-significant 0 bit gives us the length of
|
||||
* a multi-octet encoding.
|
||||
*/
|
||||
for (clen = 0; cx & 0x80; ++clen)
|
||||
cx <<= 1;
|
||||
/*
|
||||
* If the 0 bit is in position 8, this is a valid single-octet
|
||||
* encoding. If the 0 bit is in position 7 or positions 1-3, the
|
||||
* encoding is invalid.
|
||||
* In either case, we just return the first octet.
|
||||
*/
|
||||
if (clen < 2 || clen > 4)
|
||||
return c0;
|
||||
/* Get the bits from the first octet. */
|
||||
c32 = cx >> clen--;
|
||||
for (i = 0; i < clen; ++i) {
|
||||
/* Trailing octets must have 10 in most significant bits. */
|
||||
cx = (*s8)[i] ^ 0x80;
|
||||
if (cx & 0xc0)
|
||||
return c0;
|
||||
c32 = (c32 << 6) | cx;
|
||||
}
|
||||
/*
|
||||
* Check for validity:
|
||||
* - The character must be in the Unicode range.
|
||||
* - It must not be a surrogate.
|
||||
* - It must be encoded using the correct number of octets.
|
||||
*/
|
||||
if (c32 > 0x10ffff ||
|
||||
(c32 & 0xf800) == 0xd800 ||
|
||||
clen != (c32 >= 0x80) + (c32 >= 0x800) + (c32 >= 0x10000))
|
||||
return c0;
|
||||
*s8 += clen;
|
||||
return c32;
|
||||
}
|
||||
|
||||
void efi_puts(const char *str)
|
||||
{
|
||||
efi_char16_t buf[128];
|
||||
size_t pos = 0, lim = ARRAY_SIZE(buf);
|
||||
const u8 *s8 = (const u8 *)str;
|
||||
u32 c32;
|
||||
|
||||
while (*s8) {
|
||||
if (*s8 == '\n')
|
||||
buf[pos++] = L'\r';
|
||||
c32 = utf8_to_utf32(&s8);
|
||||
if (c32 < 0x10000) {
|
||||
/* Characters in plane 0 use a single word. */
|
||||
buf[pos++] = c32;
|
||||
} else {
|
||||
/*
|
||||
* Characters in other planes encode into a surrogate
|
||||
* pair.
|
||||
*/
|
||||
buf[pos++] = (0xd800 - (0x10000 >> 10)) + (c32 >> 10);
|
||||
buf[pos++] = 0xdc00 + (c32 & 0x3ff);
|
||||
}
|
||||
if (*s8 == '\0' || pos >= lim - 2) {
|
||||
buf[pos] = L'\0';
|
||||
efi_char16_puts(buf);
|
||||
pos = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int efi_printk(const char *fmt, ...)
|
||||
{
|
||||
char printf_buf[256];
|
||||
va_list args;
|
||||
int printed;
|
||||
int loglevel = printk_get_level(fmt);
|
||||
|
||||
switch (loglevel) {
|
||||
case '0' ... '9':
|
||||
loglevel -= '0';
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* Use loglevel -1 for cases where we just want to print to
|
||||
* the screen.
|
||||
*/
|
||||
loglevel = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (loglevel >= efi_loglevel)
|
||||
return 0;
|
||||
|
||||
if (loglevel >= 0)
|
||||
efi_puts("EFI stub: ");
|
||||
|
||||
fmt = printk_skip_level(fmt);
|
||||
|
||||
va_start(args, fmt);
|
||||
printed = vsnprintf(printf_buf, sizeof(printf_buf), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
efi_puts(printf_buf);
|
||||
if (printed >= sizeof(printf_buf)) {
|
||||
efi_puts("[Message truncated]\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -91,7 +182,7 @@ efi_status_t efi_parse_options(char const *cmdline)
|
||||
if (!strcmp(param, "nokaslr")) {
|
||||
efi_nokaslr = true;
|
||||
} else if (!strcmp(param, "quiet")) {
|
||||
efi_quiet = true;
|
||||
efi_loglevel = CONSOLE_LOGLEVEL_QUIET;
|
||||
} else if (!strcmp(param, "noinitrd")) {
|
||||
efi_noinitrd = true;
|
||||
} else if (!strcmp(param, "efi") && val) {
|
||||
@ -105,104 +196,91 @@ efi_status_t efi_parse_options(char const *cmdline)
|
||||
efi_disable_pci_dma = true;
|
||||
if (parse_option_str(val, "no_disable_early_pci_dma"))
|
||||
efi_disable_pci_dma = false;
|
||||
if (parse_option_str(val, "debug"))
|
||||
efi_loglevel = CONSOLE_LOGLEVEL_DEBUG;
|
||||
} else if (!strcmp(param, "video") &&
|
||||
val && strstarts(val, "efifb:")) {
|
||||
efi_parse_option_graphics(val + strlen("efifb:"));
|
||||
}
|
||||
}
|
||||
efi_bs_call(free_pool, buf);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the number of UTF-8 bytes corresponding to an UTF-16 character.
|
||||
* This overestimates for surrogates, but that is okay.
|
||||
*/
|
||||
static int efi_utf8_bytes(u16 c)
|
||||
{
|
||||
return 1 + (c >= 0x80) + (c >= 0x800);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an UTF-16 string, not necessarily null terminated, to UTF-8.
|
||||
*/
|
||||
static u8 *efi_utf16_to_utf8(u8 *dst, const u16 *src, int n)
|
||||
{
|
||||
unsigned int c;
|
||||
|
||||
while (n--) {
|
||||
c = *src++;
|
||||
if (n && c >= 0xd800 && c <= 0xdbff &&
|
||||
*src >= 0xdc00 && *src <= 0xdfff) {
|
||||
c = 0x10000 + ((c & 0x3ff) << 10) + (*src & 0x3ff);
|
||||
src++;
|
||||
n--;
|
||||
}
|
||||
if (c >= 0xd800 && c <= 0xdfff)
|
||||
c = 0xfffd; /* Unmatched surrogate */
|
||||
if (c < 0x80) {
|
||||
*dst++ = c;
|
||||
continue;
|
||||
}
|
||||
if (c < 0x800) {
|
||||
*dst++ = 0xc0 + (c >> 6);
|
||||
goto t1;
|
||||
}
|
||||
if (c < 0x10000) {
|
||||
*dst++ = 0xe0 + (c >> 12);
|
||||
goto t2;
|
||||
}
|
||||
*dst++ = 0xf0 + (c >> 18);
|
||||
*dst++ = 0x80 + ((c >> 12) & 0x3f);
|
||||
t2:
|
||||
*dst++ = 0x80 + ((c >> 6) & 0x3f);
|
||||
t1:
|
||||
*dst++ = 0x80 + (c & 0x3f);
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the unicode UEFI command line to ASCII to pass to kernel.
|
||||
* Size of memory allocated return in *cmd_line_len.
|
||||
* Returns NULL on error.
|
||||
*/
|
||||
char *efi_convert_cmdline(efi_loaded_image_t *image,
|
||||
int *cmd_line_len, unsigned long max_addr)
|
||||
char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len)
|
||||
{
|
||||
const u16 *s2;
|
||||
u8 *s1 = NULL;
|
||||
unsigned long cmdline_addr = 0;
|
||||
int load_options_chars = efi_table_attr(image, load_options_size) / 2;
|
||||
int options_chars = efi_table_attr(image, load_options_size) / 2;
|
||||
const u16 *options = efi_table_attr(image, load_options);
|
||||
int options_bytes = 0; /* UTF-8 bytes */
|
||||
int options_chars = 0; /* UTF-16 chars */
|
||||
int options_bytes = 0, safe_options_bytes = 0; /* UTF-8 bytes */
|
||||
bool in_quote = false;
|
||||
efi_status_t status;
|
||||
u16 zero = 0;
|
||||
|
||||
if (options) {
|
||||
s2 = options;
|
||||
while (*s2 && *s2 != '\n'
|
||||
&& options_chars < load_options_chars) {
|
||||
options_bytes += efi_utf8_bytes(*s2++);
|
||||
options_chars++;
|
||||
}
|
||||
}
|
||||
while (options_bytes < COMMAND_LINE_SIZE && options_chars--) {
|
||||
u16 c = *s2++;
|
||||
|
||||
if (!options_chars) {
|
||||
/* No command line options, so return empty string*/
|
||||
options = &zero;
|
||||
if (c < 0x80) {
|
||||
if (c == L'\0' || c == L'\n')
|
||||
break;
|
||||
if (c == L'"')
|
||||
in_quote = !in_quote;
|
||||
else if (!in_quote && isspace((char)c))
|
||||
safe_options_bytes = options_bytes;
|
||||
|
||||
options_bytes++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the number of UTF-8 bytes corresponding to a
|
||||
* UTF-16 character.
|
||||
* The first part handles everything in the BMP.
|
||||
*/
|
||||
options_bytes += 2 + (c >= 0x800);
|
||||
/*
|
||||
* Add one more byte for valid surrogate pairs. Invalid
|
||||
* surrogates will be replaced with 0xfffd and take up
|
||||
* only 3 bytes.
|
||||
*/
|
||||
if ((c & 0xfc00) == 0xd800) {
|
||||
/*
|
||||
* If the very last word is a high surrogate,
|
||||
* we must ignore it since we can't access the
|
||||
* low surrogate.
|
||||
*/
|
||||
if (!options_chars) {
|
||||
options_bytes -= 3;
|
||||
} else if ((*s2 & 0xfc00) == 0xdc00) {
|
||||
options_bytes++;
|
||||
options_chars--;
|
||||
s2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (options_bytes >= COMMAND_LINE_SIZE) {
|
||||
options_bytes = safe_options_bytes;
|
||||
efi_err("Command line is too long: truncated to %d bytes\n",
|
||||
options_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
options_bytes++; /* NUL termination */
|
||||
|
||||
status = efi_allocate_pages(options_bytes, &cmdline_addr, max_addr);
|
||||
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, options_bytes,
|
||||
(void **)&cmdline_addr);
|
||||
if (status != EFI_SUCCESS)
|
||||
return NULL;
|
||||
|
||||
s1 = (u8 *)cmdline_addr;
|
||||
s2 = (const u16 *)options;
|
||||
|
||||
s1 = efi_utf16_to_utf8(s1, s2, options_chars);
|
||||
*s1 = '\0';
|
||||
snprintf((char *)cmdline_addr, options_bytes, "%.*ls",
|
||||
options_bytes - 1, options);
|
||||
|
||||
*cmd_line_len = options_bytes;
|
||||
return (char *)cmdline_addr;
|
||||
@ -285,8 +363,8 @@ fail:
|
||||
|
||||
void *get_efi_config_table(efi_guid_t guid)
|
||||
{
|
||||
unsigned long tables = efi_table_attr(efi_system_table(), tables);
|
||||
int nr_tables = efi_table_attr(efi_system_table(), nr_tables);
|
||||
unsigned long tables = efi_table_attr(efi_system_table, tables);
|
||||
int nr_tables = efi_table_attr(efi_system_table, nr_tables);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr_tables; i++) {
|
||||
@ -301,12 +379,6 @@ void *get_efi_config_table(efi_guid_t guid)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void efi_char16_printk(efi_char16_t *str)
|
||||
{
|
||||
efi_call_proto(efi_table_attr(efi_system_table(), con_out),
|
||||
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
|
||||
@ -348,6 +420,7 @@ static const struct {
|
||||
* %EFI_OUT_OF_RESOURCES if memory allocation failed
|
||||
* %EFI_LOAD_ERROR in all other cases
|
||||
*/
|
||||
static
|
||||
efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr,
|
||||
unsigned long *load_size,
|
||||
unsigned long max)
|
||||
@ -360,9 +433,6 @@ efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr,
|
||||
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)
|
||||
@ -392,3 +462,80 @@ efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr,
|
||||
*load_size = initrd_size;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
efi_status_t efi_load_initrd_cmdline(efi_loaded_image_t *image,
|
||||
unsigned long *load_addr,
|
||||
unsigned long *load_size,
|
||||
unsigned long soft_limit,
|
||||
unsigned long hard_limit)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER) ||
|
||||
(IS_ENABLED(CONFIG_X86) && (!efi_is_native() || image == NULL))) {
|
||||
*load_addr = *load_size = 0;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
return handle_cmdline_files(image, L"initrd=", sizeof(L"initrd=") - 2,
|
||||
soft_limit, hard_limit,
|
||||
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)
|
||||
{
|
||||
efi_status_t status;
|
||||
|
||||
if (!load_addr || !load_size)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
status = efi_load_initrd_dev_path(load_addr, load_size, hard_limit);
|
||||
if (status == EFI_SUCCESS) {
|
||||
efi_info("Loaded initrd from LINUX_EFI_INITRD_MEDIA_GUID device path\n");
|
||||
} else if (status == EFI_NOT_FOUND) {
|
||||
status = efi_load_initrd_cmdline(image, load_addr, load_size,
|
||||
soft_limit, hard_limit);
|
||||
if (status == EFI_SUCCESS && *load_size > 0)
|
||||
efi_info("Loaded initrd from command line option\n");
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
efi_status_t efi_wait_for_key(unsigned long usec, efi_input_key_t *key)
|
||||
{
|
||||
efi_event_t events[2], timer;
|
||||
unsigned long index;
|
||||
efi_simple_text_input_protocol_t *con_in;
|
||||
efi_status_t status;
|
||||
|
||||
con_in = efi_table_attr(efi_system_table, con_in);
|
||||
if (!con_in)
|
||||
return EFI_UNSUPPORTED;
|
||||
efi_set_event_at(events, 0, efi_table_attr(con_in, wait_for_key));
|
||||
|
||||
status = efi_bs_call(create_event, EFI_EVT_TIMER, 0, NULL, NULL, &timer);
|
||||
if (status != EFI_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = efi_bs_call(set_timer, timer, EfiTimerRelative,
|
||||
EFI_100NSEC_PER_USEC * usec);
|
||||
if (status != EFI_SUCCESS)
|
||||
return status;
|
||||
efi_set_event_at(events, 1, timer);
|
||||
|
||||
status = efi_bs_call(wait_for_event, 2, events, &index);
|
||||
if (status == EFI_SUCCESS) {
|
||||
if (index == 0)
|
||||
status = efi_call_proto(con_in, read_keystroke, key);
|
||||
else
|
||||
status = EFI_TIMEOUT;
|
||||
}
|
||||
|
||||
efi_bs_call(close_event, timer);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
@ -36,14 +36,9 @@
|
||||
#endif
|
||||
|
||||
static u64 virtmap_base = EFI_RT_VIRTUAL_BASE;
|
||||
static bool __efistub_global flat_va_mapping;
|
||||
static bool flat_va_mapping;
|
||||
|
||||
static efi_system_table_t *__efistub_global sys_table;
|
||||
|
||||
__pure efi_system_table_t *efi_system_table(void)
|
||||
{
|
||||
return sys_table;
|
||||
}
|
||||
const efi_system_table_t *efi_system_table;
|
||||
|
||||
static struct screen_info *setup_graphics(void)
|
||||
{
|
||||
@ -69,7 +64,7 @@ static struct screen_info *setup_graphics(void)
|
||||
return si;
|
||||
}
|
||||
|
||||
void install_memreserve_table(void)
|
||||
static void install_memreserve_table(void)
|
||||
{
|
||||
struct linux_efi_memreserve *rsv;
|
||||
efi_guid_t memreserve_table_guid = LINUX_EFI_MEMRESERVE_TABLE_GUID;
|
||||
@ -78,7 +73,7 @@ void install_memreserve_table(void)
|
||||
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, sizeof(*rsv),
|
||||
(void **)&rsv);
|
||||
if (status != EFI_SUCCESS) {
|
||||
pr_efi_err("Failed to allocate memreserve entry!\n");
|
||||
efi_err("Failed to allocate memreserve entry!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -89,7 +84,7 @@ void install_memreserve_table(void)
|
||||
status = efi_bs_call(install_configuration_table,
|
||||
&memreserve_table_guid, rsv);
|
||||
if (status != EFI_SUCCESS)
|
||||
pr_efi_err("Failed to install memreserve config table!\n");
|
||||
efi_err("Failed to install memreserve config table!\n");
|
||||
}
|
||||
|
||||
static unsigned long get_dram_base(void)
|
||||
@ -149,7 +144,8 @@ asmlinkage void __noreturn efi_enter_kernel(unsigned long entrypoint,
|
||||
* for both archictectures, with the arch-specific code provided in the
|
||||
* handle_kernel_image() function.
|
||||
*/
|
||||
efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
|
||||
efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
|
||||
efi_system_table_t *sys_table_arg)
|
||||
{
|
||||
efi_loaded_image_t *image;
|
||||
efi_status_t status;
|
||||
@ -171,10 +167,10 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
|
||||
efi_properties_table_t *prop_tbl;
|
||||
unsigned long max_addr;
|
||||
|
||||
sys_table = sys_table_arg;
|
||||
efi_system_table = sys_table_arg;
|
||||
|
||||
/* Check if we were booted by the EFI firmware */
|
||||
if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) {
|
||||
if (efi_system_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) {
|
||||
status = EFI_INVALID_PARAMETER;
|
||||
goto fail;
|
||||
}
|
||||
@ -188,16 +184,16 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
|
||||
* information about the running image, such as size and the command
|
||||
* line.
|
||||
*/
|
||||
status = sys_table->boottime->handle_protocol(handle,
|
||||
status = efi_system_table->boottime->handle_protocol(handle,
|
||||
&loaded_image_proto, (void *)&image);
|
||||
if (status != EFI_SUCCESS) {
|
||||
pr_efi_err("Failed to get loaded image protocol\n");
|
||||
efi_err("Failed to get loaded image protocol\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dram_base = get_dram_base();
|
||||
if (dram_base == EFI_ERROR) {
|
||||
pr_efi_err("Failed to find DRAM base\n");
|
||||
efi_err("Failed to find DRAM base\n");
|
||||
status = EFI_LOAD_ERROR;
|
||||
goto fail;
|
||||
}
|
||||
@ -207,22 +203,32 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
|
||||
* protocol. We are going to copy the command line into the
|
||||
* device tree, so this can be allocated anywhere.
|
||||
*/
|
||||
cmdline_ptr = efi_convert_cmdline(image, &cmdline_size, ULONG_MAX);
|
||||
cmdline_ptr = efi_convert_cmdline(image, &cmdline_size);
|
||||
if (!cmdline_ptr) {
|
||||
pr_efi_err("getting command line via LOADED_IMAGE_PROTOCOL\n");
|
||||
efi_err("getting command line via LOADED_IMAGE_PROTOCOL\n");
|
||||
status = EFI_OUT_OF_RESOURCES;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_CMDLINE_EXTEND) ||
|
||||
IS_ENABLED(CONFIG_CMDLINE_FORCE) ||
|
||||
cmdline_size == 0)
|
||||
efi_parse_options(CONFIG_CMDLINE);
|
||||
cmdline_size == 0) {
|
||||
status = efi_parse_options(CONFIG_CMDLINE);
|
||||
if (status != EFI_SUCCESS) {
|
||||
efi_err("Failed to parse options\n");
|
||||
goto fail_free_cmdline;
|
||||
}
|
||||
}
|
||||
|
||||
if (!IS_ENABLED(CONFIG_CMDLINE_FORCE) && cmdline_size > 0)
|
||||
efi_parse_options(cmdline_ptr);
|
||||
if (!IS_ENABLED(CONFIG_CMDLINE_FORCE) && cmdline_size > 0) {
|
||||
status = efi_parse_options(cmdline_ptr);
|
||||
if (status != EFI_SUCCESS) {
|
||||
efi_err("Failed to parse options\n");
|
||||
goto fail_free_cmdline;
|
||||
}
|
||||
}
|
||||
|
||||
pr_efi("Booting Linux Kernel...\n");
|
||||
efi_info("Booting Linux Kernel...\n");
|
||||
|
||||
si = setup_graphics();
|
||||
|
||||
@ -231,8 +237,8 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
|
||||
&reserve_size,
|
||||
dram_base, image);
|
||||
if (status != EFI_SUCCESS) {
|
||||
pr_efi_err("Failed to relocate kernel\n");
|
||||
goto fail_free_cmdline;
|
||||
efi_err("Failed to relocate kernel\n");
|
||||
goto fail_free_screeninfo;
|
||||
}
|
||||
|
||||
efi_retrieve_tpm2_eventlog();
|
||||
@ -250,42 +256,34 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
|
||||
if (!IS_ENABLED(CONFIG_EFI_ARMSTUB_DTB_LOADER) ||
|
||||
secure_boot != efi_secureboot_mode_disabled) {
|
||||
if (strstr(cmdline_ptr, "dtb="))
|
||||
pr_efi("Ignoring DTB from command line.\n");
|
||||
efi_err("Ignoring DTB from command line.\n");
|
||||
} else {
|
||||
status = efi_load_dtb(image, &fdt_addr, &fdt_size);
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
pr_efi_err("Failed to load device tree!\n");
|
||||
efi_err("Failed to load device tree!\n");
|
||||
goto fail_free_image;
|
||||
}
|
||||
}
|
||||
|
||||
if (fdt_addr) {
|
||||
pr_efi("Using DTB from command line\n");
|
||||
efi_info("Using DTB from command line\n");
|
||||
} else {
|
||||
/* Look for a device tree configuration table entry. */
|
||||
fdt_addr = (uintptr_t)get_fdt(&fdt_size);
|
||||
if (fdt_addr)
|
||||
pr_efi("Using DTB from configuration table\n");
|
||||
efi_info("Using DTB from configuration table\n");
|
||||
}
|
||||
|
||||
if (!fdt_addr)
|
||||
pr_efi("Generating empty DTB\n");
|
||||
efi_info("Generating empty DTB\n");
|
||||
|
||||
if (!noinitrd()) {
|
||||
if (!efi_noinitrd) {
|
||||
max_addr = efi_get_max_initrd_addr(dram_base, image_addr);
|
||||
status = efi_load_initrd_dev_path(&initrd_addr, &initrd_size,
|
||||
max_addr);
|
||||
if (status == EFI_SUCCESS) {
|
||||
pr_efi("Loaded initrd from LINUX_EFI_INITRD_MEDIA_GUID device path\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");
|
||||
}
|
||||
status = efi_load_initrd(image, &initrd_addr, &initrd_size,
|
||||
ULONG_MAX, max_addr);
|
||||
if (status != EFI_SUCCESS)
|
||||
pr_efi_err("Failed to load initrd!\n");
|
||||
efi_err("Failed to load initrd!\n");
|
||||
}
|
||||
|
||||
efi_random_get_seed();
|
||||
@ -303,7 +301,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
|
||||
EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA);
|
||||
|
||||
/* hibernation expects the runtime regions to stay in the same place */
|
||||
if (!IS_ENABLED(CONFIG_HIBERNATION) && !nokaslr() && !flat_va_mapping) {
|
||||
if (!IS_ENABLED(CONFIG_HIBERNATION) && !efi_nokaslr && !flat_va_mapping) {
|
||||
/*
|
||||
* Randomize the base of the UEFI runtime services region.
|
||||
* Preserve the 2 MB alignment of the region by taking a
|
||||
@ -335,7 +333,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
|
||||
/* not reached */
|
||||
|
||||
fail_free_initrd:
|
||||
pr_efi_err("Failed to update FDT and exit boot services\n");
|
||||
efi_err("Failed to update FDT and exit boot services\n");
|
||||
|
||||
efi_free(initrd_size, initrd_addr);
|
||||
efi_free(fdt_size, fdt_addr);
|
||||
@ -343,9 +341,10 @@ fail_free_initrd:
|
||||
fail_free_image:
|
||||
efi_free(image_size, image_addr);
|
||||
efi_free(reserve_size, reserve_addr);
|
||||
fail_free_cmdline:
|
||||
fail_free_screeninfo:
|
||||
free_screen_info(si);
|
||||
efi_free(cmdline_size, (unsigned long)cmdline_ptr);
|
||||
fail_free_cmdline:
|
||||
efi_bs_call(free_pool, cmdline_ptr);
|
||||
fail:
|
||||
return status;
|
||||
}
|
||||
@ -376,7 +375,7 @@ void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size,
|
||||
size = in->num_pages * EFI_PAGE_SIZE;
|
||||
|
||||
in->virt_addr = in->phys_addr;
|
||||
if (novamap()) {
|
||||
if (efi_novamap) {
|
||||
continue;
|
||||
}
|
||||
|
@ -3,6 +3,13 @@
|
||||
#ifndef _DRIVERS_FIRMWARE_EFI_EFISTUB_H
|
||||
#define _DRIVERS_FIRMWARE_EFI_EFISTUB_H
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/efi.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kern_levels.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/efi.h>
|
||||
|
||||
/* error code which can't be mistaken for valid address */
|
||||
#define EFI_ERROR (~0UL)
|
||||
|
||||
@ -25,25 +32,33 @@
|
||||
#define EFI_ALLOC_ALIGN EFI_PAGE_SIZE
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ARM) || defined(CONFIG_X86)
|
||||
#define __efistub_global __section(.data)
|
||||
#else
|
||||
#define __efistub_global
|
||||
extern bool efi_nochunk;
|
||||
extern bool efi_nokaslr;
|
||||
extern bool efi_noinitrd;
|
||||
extern int efi_loglevel;
|
||||
extern bool efi_novamap;
|
||||
|
||||
extern const efi_system_table_t *efi_system_table;
|
||||
|
||||
efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
|
||||
efi_system_table_t *sys_table_arg);
|
||||
|
||||
#ifndef ARCH_HAS_EFISTUB_WRAPPERS
|
||||
|
||||
#define efi_is_native() (true)
|
||||
#define efi_bs_call(func, ...) efi_system_table->boottime->func(__VA_ARGS__)
|
||||
#define efi_rt_call(func, ...) efi_system_table->runtime->func(__VA_ARGS__)
|
||||
#define efi_table_attr(inst, attr) (inst->attr)
|
||||
#define efi_call_proto(inst, func, ...) inst->func(inst, ##__VA_ARGS__)
|
||||
|
||||
#endif
|
||||
|
||||
extern bool __pure nochunk(void);
|
||||
extern bool __pure nokaslr(void);
|
||||
extern bool __pure noinitrd(void);
|
||||
extern bool __pure is_quiet(void);
|
||||
extern bool __pure novamap(void);
|
||||
|
||||
extern __pure efi_system_table_t *efi_system_table(void);
|
||||
|
||||
#define pr_efi(msg) do { \
|
||||
if (!is_quiet()) efi_printk("EFI stub: "msg); \
|
||||
} while (0)
|
||||
|
||||
#define pr_efi_err(msg) efi_printk("EFI stub: ERROR: "msg)
|
||||
#define efi_info(fmt, ...) \
|
||||
efi_printk(KERN_INFO fmt, ##__VA_ARGS__)
|
||||
#define efi_err(fmt, ...) \
|
||||
efi_printk(KERN_ERR "ERROR: " fmt, ##__VA_ARGS__)
|
||||
#define efi_debug(fmt, ...) \
|
||||
efi_printk(KERN_DEBUG "DEBUG: " fmt, ##__VA_ARGS__)
|
||||
|
||||
/* Helper macros for the usual case of using simple C variables: */
|
||||
#ifndef fdt_setprop_inplace_var
|
||||
@ -77,6 +92,13 @@ extern __pure efi_system_table_t *efi_system_table(void);
|
||||
((handle = efi_get_handle_at((array), i)) || true); \
|
||||
i++)
|
||||
|
||||
static inline
|
||||
void efi_set_u64_split(u64 data, u32 *lo, u32 *hi)
|
||||
{
|
||||
*lo = lower_32_bits(data);
|
||||
*hi = upper_32_bits(data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocation types for calls to boottime->allocate_pages.
|
||||
*/
|
||||
@ -92,6 +114,16 @@ extern __pure efi_system_table_t *efi_system_table(void);
|
||||
#define EFI_LOCATE_BY_REGISTER_NOTIFY 1
|
||||
#define EFI_LOCATE_BY_PROTOCOL 2
|
||||
|
||||
/*
|
||||
* boottime->stall takes the time period in microseconds
|
||||
*/
|
||||
#define EFI_USEC_PER_SEC 1000000
|
||||
|
||||
/*
|
||||
* boottime->set_timer takes the time in 100ns units
|
||||
*/
|
||||
#define EFI_100NSEC_PER_USEC ((u64)10)
|
||||
|
||||
/*
|
||||
* An efi_boot_memmap is used by efi_get_memory_map() to return the
|
||||
* EFI memory map in a dynamically allocated buffer.
|
||||
@ -116,6 +148,39 @@ struct efi_boot_memmap {
|
||||
|
||||
typedef struct efi_generic_dev_path efi_device_path_protocol_t;
|
||||
|
||||
typedef void *efi_event_t;
|
||||
/* Note that notifications won't work in mixed mode */
|
||||
typedef void (__efiapi *efi_event_notify_t)(efi_event_t, void *);
|
||||
|
||||
#define EFI_EVT_TIMER 0x80000000U
|
||||
#define EFI_EVT_RUNTIME 0x40000000U
|
||||
#define EFI_EVT_NOTIFY_WAIT 0x00000100U
|
||||
#define EFI_EVT_NOTIFY_SIGNAL 0x00000200U
|
||||
|
||||
/*
|
||||
* boottime->wait_for_event takes an array of events as input.
|
||||
* Provide a helper to set it up correctly for mixed mode.
|
||||
*/
|
||||
static inline
|
||||
void efi_set_event_at(efi_event_t *events, size_t idx, efi_event_t event)
|
||||
{
|
||||
if (efi_is_native())
|
||||
events[idx] = event;
|
||||
else
|
||||
((u32 *)events)[idx] = (u32)(unsigned long)event;
|
||||
}
|
||||
|
||||
#define EFI_TPL_APPLICATION 4
|
||||
#define EFI_TPL_CALLBACK 8
|
||||
#define EFI_TPL_NOTIFY 16
|
||||
#define EFI_TPL_HIGH_LEVEL 31
|
||||
|
||||
typedef enum {
|
||||
EfiTimerCancel,
|
||||
EfiTimerPeriodic,
|
||||
EfiTimerRelative
|
||||
} EFI_TIMER_DELAY;
|
||||
|
||||
/*
|
||||
* EFI Boot Services table
|
||||
*/
|
||||
@ -134,11 +199,16 @@ union efi_boot_services {
|
||||
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;
|
||||
efi_status_t (__efiapi *create_event)(u32, unsigned long,
|
||||
efi_event_notify_t, void *,
|
||||
efi_event_t *);
|
||||
efi_status_t (__efiapi *set_timer)(efi_event_t,
|
||||
EFI_TIMER_DELAY, u64);
|
||||
efi_status_t (__efiapi *wait_for_event)(unsigned long,
|
||||
efi_event_t *,
|
||||
unsigned long *);
|
||||
void *signal_event;
|
||||
void *close_event;
|
||||
efi_status_t (__efiapi *close_event)(efi_event_t);
|
||||
void *check_event;
|
||||
void *install_protocol_interface;
|
||||
void *reinstall_protocol_interface;
|
||||
@ -165,7 +235,7 @@ union efi_boot_services {
|
||||
efi_status_t (__efiapi *exit_boot_services)(efi_handle_t,
|
||||
unsigned long);
|
||||
void *get_next_monotonic_count;
|
||||
void *stall;
|
||||
efi_status_t (__efiapi *stall)(unsigned long);
|
||||
void *set_watchdog_timer;
|
||||
void *connect_controller;
|
||||
efi_status_t (__efiapi *disconnect_controller)(efi_handle_t,
|
||||
@ -250,6 +320,27 @@ union efi_uga_draw_protocol {
|
||||
} mixed_mode;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
u16 scan_code;
|
||||
efi_char16_t unicode_char;
|
||||
} efi_input_key_t;
|
||||
|
||||
union efi_simple_text_input_protocol {
|
||||
struct {
|
||||
void *reset;
|
||||
efi_status_t (__efiapi *read_keystroke)(efi_simple_text_input_protocol_t *,
|
||||
efi_input_key_t *);
|
||||
efi_event_t wait_for_key;
|
||||
};
|
||||
struct {
|
||||
u32 reset;
|
||||
u32 read_keystroke;
|
||||
u32 wait_for_key;
|
||||
} mixed_mode;
|
||||
};
|
||||
|
||||
efi_status_t efi_wait_for_key(unsigned long usec, efi_input_key_t *key);
|
||||
|
||||
union efi_simple_text_output_protocol {
|
||||
struct {
|
||||
void *reset;
|
||||
@ -311,8 +402,10 @@ typedef union efi_graphics_output_protocol efi_graphics_output_protocol_t;
|
||||
|
||||
union efi_graphics_output_protocol {
|
||||
struct {
|
||||
void *query_mode;
|
||||
void *set_mode;
|
||||
efi_status_t (__efiapi *query_mode)(efi_graphics_output_protocol_t *,
|
||||
u32, unsigned long *,
|
||||
efi_graphics_output_mode_info_t **);
|
||||
efi_status_t (__efiapi *set_mode) (efi_graphics_output_protocol_t *, u32);
|
||||
void *blt;
|
||||
efi_graphics_output_protocol_mode_t *mode;
|
||||
};
|
||||
@ -600,8 +693,6 @@ efi_status_t efi_exit_boot_services(void *handle,
|
||||
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,
|
||||
unsigned long *new_fdt_addr,
|
||||
unsigned long max_addr,
|
||||
@ -625,33 +716,24 @@ efi_status_t check_platform_features(void);
|
||||
|
||||
void *get_efi_config_table(efi_guid_t guid);
|
||||
|
||||
void efi_printk(char *str);
|
||||
/* NOTE: These functions do not print a trailing newline after the string */
|
||||
void efi_char16_puts(efi_char16_t *);
|
||||
void efi_puts(const char *str);
|
||||
|
||||
__printf(1, 2) int efi_printk(char const *fmt, ...);
|
||||
|
||||
void efi_free(unsigned long size, unsigned long addr);
|
||||
|
||||
char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len,
|
||||
unsigned long max_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_allocate_pages(unsigned long size, unsigned long *addr,
|
||||
unsigned long max);
|
||||
|
||||
efi_status_t efi_allocate_pages_aligned(unsigned long size, unsigned long *addr,
|
||||
unsigned long max, unsigned long align);
|
||||
|
||||
efi_status_t efi_relocate_kernel(unsigned long *image_addr,
|
||||
unsigned long image_size,
|
||||
unsigned long alloc_size,
|
||||
@ -661,12 +743,27 @@ efi_status_t efi_relocate_kernel(unsigned long *image_addr,
|
||||
|
||||
efi_status_t efi_parse_options(char const *cmdline);
|
||||
|
||||
void efi_parse_option_graphics(char *option);
|
||||
|
||||
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 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);
|
||||
|
||||
|
||||
static inline 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,
|
||||
@ -674,8 +771,4 @@ efi_status_t efi_load_initrd(efi_loaded_image_t *image,
|
||||
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
|
||||
|
@ -39,7 +39,7 @@ static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size,
|
||||
/* Do some checks on provided FDT, if it exists: */
|
||||
if (orig_fdt) {
|
||||
if (fdt_check_header(orig_fdt)) {
|
||||
pr_efi_err("Device Tree header not valid!\n");
|
||||
efi_err("Device Tree header not valid!\n");
|
||||
return EFI_LOAD_ERROR;
|
||||
}
|
||||
/*
|
||||
@ -47,7 +47,7 @@ static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size,
|
||||
* configuration table:
|
||||
*/
|
||||
if (orig_fdt_size && fdt_totalsize(orig_fdt) > orig_fdt_size) {
|
||||
pr_efi_err("Truncated device tree! foo!\n");
|
||||
efi_err("Truncated device tree! foo!\n");
|
||||
return EFI_LOAD_ERROR;
|
||||
}
|
||||
}
|
||||
@ -110,7 +110,7 @@ static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size,
|
||||
|
||||
/* Add FDT entries for EFI runtime services in chosen node. */
|
||||
node = fdt_subnode_offset(fdt, 0, "chosen");
|
||||
fdt_val64 = cpu_to_fdt64((u64)(unsigned long)efi_system_table());
|
||||
fdt_val64 = cpu_to_fdt64((u64)(unsigned long)efi_system_table);
|
||||
|
||||
status = fdt_setprop_var(fdt, node, "linux,uefi-system-table", fdt_val64);
|
||||
if (status)
|
||||
@ -270,16 +270,16 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
|
||||
*/
|
||||
status = efi_get_memory_map(&map);
|
||||
if (status != EFI_SUCCESS) {
|
||||
pr_efi_err("Unable to retrieve UEFI memory map.\n");
|
||||
efi_err("Unable to retrieve UEFI memory map.\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
pr_efi("Exiting boot services and installing virtual address map...\n");
|
||||
efi_info("Exiting boot services and installing virtual address map...\n");
|
||||
|
||||
map.map = &memory_map;
|
||||
status = efi_allocate_pages(MAX_FDT_SIZE, new_fdt_addr, max_addr);
|
||||
if (status != EFI_SUCCESS) {
|
||||
pr_efi_err("Unable to allocate memory for new device tree.\n");
|
||||
efi_err("Unable to allocate memory for new device tree.\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -296,7 +296,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
|
||||
initrd_addr, initrd_size);
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
pr_efi_err("Unable to construct new device tree.\n");
|
||||
efi_err("Unable to construct new device tree.\n");
|
||||
goto fail_free_new_fdt;
|
||||
}
|
||||
|
||||
@ -310,11 +310,11 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
|
||||
if (status == EFI_SUCCESS) {
|
||||
efi_set_virtual_address_map_t *svam;
|
||||
|
||||
if (novamap())
|
||||
if (efi_novamap)
|
||||
return EFI_SUCCESS;
|
||||
|
||||
/* Install the new virtual address map */
|
||||
svam = efi_system_table()->runtime->set_virtual_address_map;
|
||||
svam = efi_system_table->runtime->set_virtual_address_map;
|
||||
status = svam(runtime_entry_count * desc_size, desc_size,
|
||||
desc_ver, runtime_map);
|
||||
|
||||
@ -342,13 +342,13 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
pr_efi_err("Exit boot services failed.\n");
|
||||
efi_err("Exit boot services failed.\n");
|
||||
|
||||
fail_free_new_fdt:
|
||||
efi_free(MAX_FDT_SIZE, *new_fdt_addr);
|
||||
|
||||
fail:
|
||||
efi_system_table()->boottime->free_pool(runtime_map);
|
||||
efi_system_table->boottime->free_pool(runtime_map);
|
||||
|
||||
return EFI_LOAD_ERROR;
|
||||
}
|
||||
@ -363,7 +363,7 @@ void *get_fdt(unsigned long *fdt_size)
|
||||
return NULL;
|
||||
|
||||
if (fdt_check_header(fdt) != 0) {
|
||||
pr_efi_err("Invalid header detected on UEFI supplied FDT, ignoring ...\n");
|
||||
efi_err("Invalid header detected on UEFI supplied FDT, ignoring ...\n");
|
||||
return NULL;
|
||||
}
|
||||
*fdt_size = fdt_totalsize(fdt);
|
||||
|
@ -46,16 +46,14 @@ static efi_status_t efi_open_file(efi_file_protocol_t *volume,
|
||||
|
||||
status = volume->open(volume, &fh, fi->filename, EFI_FILE_MODE_READ, 0);
|
||||
if (status != EFI_SUCCESS) {
|
||||
pr_efi_err("Failed to open file: ");
|
||||
efi_char16_printk(fi->filename);
|
||||
efi_printk("\n");
|
||||
efi_err("Failed to open file: %ls\n", fi->filename);
|
||||
return status;
|
||||
}
|
||||
|
||||
info_sz = sizeof(struct finfo);
|
||||
status = fh->get_info(fh, &info_guid, &info_sz, fi);
|
||||
if (status != EFI_SUCCESS) {
|
||||
pr_efi_err("Failed to get file info\n");
|
||||
efi_err("Failed to get file info\n");
|
||||
fh->close(fh);
|
||||
return status;
|
||||
}
|
||||
@ -75,13 +73,13 @@ static efi_status_t efi_open_volume(efi_loaded_image_t *image,
|
||||
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");
|
||||
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");
|
||||
efi_err("Failed to open volume\n");
|
||||
|
||||
return status;
|
||||
}
|
||||
@ -121,13 +119,13 @@ static int find_file_option(const efi_char16_t *cmdline, int cmdline_len,
|
||||
* 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)
|
||||
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;
|
||||
@ -142,7 +140,7 @@ static efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
|
||||
if (!load_addr || !load_size)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
if (IS_ENABLED(CONFIG_X86) && !nochunk())
|
||||
if (IS_ENABLED(CONFIG_X86) && !efi_nochunk)
|
||||
efi_chunk_size = EFI_READ_CHUNK_SIZE;
|
||||
|
||||
alloc_addr = alloc_size = 0;
|
||||
@ -191,7 +189,7 @@ static efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
|
||||
&alloc_addr,
|
||||
hard_limit);
|
||||
if (status != EFI_SUCCESS) {
|
||||
pr_efi_err("Failed to allocate memory for files\n");
|
||||
efi_err("Failed to allocate memory for files\n");
|
||||
goto err_close_file;
|
||||
}
|
||||
|
||||
@ -215,7 +213,7 @@ static efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
|
||||
|
||||
status = file->read(file, &chunksize, addr);
|
||||
if (status != EFI_SUCCESS) {
|
||||
pr_efi_err("Failed to read file\n");
|
||||
efi_err("Failed to read file\n");
|
||||
goto err_close_file;
|
||||
}
|
||||
addr += chunksize;
|
||||
@ -239,21 +237,3 @@ err_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);
|
||||
}
|
||||
|
@ -5,169 +5,546 @@
|
||||
*
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/efi.h>
|
||||
#include <linux/screen_info.h>
|
||||
#include <linux/string.h>
|
||||
#include <asm/efi.h>
|
||||
#include <asm/setup.h>
|
||||
|
||||
#include "efistub.h"
|
||||
|
||||
static void find_bits(unsigned long mask, u8 *pos, u8 *size)
|
||||
enum efi_cmdline_option {
|
||||
EFI_CMDLINE_NONE,
|
||||
EFI_CMDLINE_MODE_NUM,
|
||||
EFI_CMDLINE_RES,
|
||||
EFI_CMDLINE_AUTO,
|
||||
EFI_CMDLINE_LIST
|
||||
};
|
||||
|
||||
static struct {
|
||||
enum efi_cmdline_option option;
|
||||
union {
|
||||
u32 mode;
|
||||
struct {
|
||||
u32 width, height;
|
||||
int format;
|
||||
u8 depth;
|
||||
} res;
|
||||
};
|
||||
} cmdline = { .option = EFI_CMDLINE_NONE };
|
||||
|
||||
static bool parse_modenum(char *option, char **next)
|
||||
{
|
||||
u8 first, len;
|
||||
u32 m;
|
||||
|
||||
first = 0;
|
||||
len = 0;
|
||||
if (!strstarts(option, "mode="))
|
||||
return false;
|
||||
option += strlen("mode=");
|
||||
m = simple_strtoull(option, &option, 0);
|
||||
if (*option && *option++ != ',')
|
||||
return false;
|
||||
cmdline.option = EFI_CMDLINE_MODE_NUM;
|
||||
cmdline.mode = m;
|
||||
|
||||
if (mask) {
|
||||
while (!(mask & 0x1)) {
|
||||
mask = mask >> 1;
|
||||
first++;
|
||||
}
|
||||
*next = option;
|
||||
return true;
|
||||
}
|
||||
|
||||
while (mask & 0x1) {
|
||||
mask = mask >> 1;
|
||||
len++;
|
||||
static bool parse_res(char *option, char **next)
|
||||
{
|
||||
u32 w, h, d = 0;
|
||||
int pf = -1;
|
||||
|
||||
if (!isdigit(*option))
|
||||
return false;
|
||||
w = simple_strtoull(option, &option, 10);
|
||||
if (*option++ != 'x' || !isdigit(*option))
|
||||
return false;
|
||||
h = simple_strtoull(option, &option, 10);
|
||||
if (*option == '-') {
|
||||
option++;
|
||||
if (strstarts(option, "rgb")) {
|
||||
option += strlen("rgb");
|
||||
pf = PIXEL_RGB_RESERVED_8BIT_PER_COLOR;
|
||||
} else if (strstarts(option, "bgr")) {
|
||||
option += strlen("bgr");
|
||||
pf = PIXEL_BGR_RESERVED_8BIT_PER_COLOR;
|
||||
} else if (isdigit(*option))
|
||||
d = simple_strtoull(option, &option, 10);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
if (*option && *option++ != ',')
|
||||
return false;
|
||||
cmdline.option = EFI_CMDLINE_RES;
|
||||
cmdline.res.width = w;
|
||||
cmdline.res.height = h;
|
||||
cmdline.res.format = pf;
|
||||
cmdline.res.depth = d;
|
||||
|
||||
*next = option;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_auto(char *option, char **next)
|
||||
{
|
||||
if (!strstarts(option, "auto"))
|
||||
return false;
|
||||
option += strlen("auto");
|
||||
if (*option && *option++ != ',')
|
||||
return false;
|
||||
cmdline.option = EFI_CMDLINE_AUTO;
|
||||
|
||||
*next = option;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_list(char *option, char **next)
|
||||
{
|
||||
if (!strstarts(option, "list"))
|
||||
return false;
|
||||
option += strlen("list");
|
||||
if (*option && *option++ != ',')
|
||||
return false;
|
||||
cmdline.option = EFI_CMDLINE_LIST;
|
||||
|
||||
*next = option;
|
||||
return true;
|
||||
}
|
||||
|
||||
void efi_parse_option_graphics(char *option)
|
||||
{
|
||||
while (*option) {
|
||||
if (parse_modenum(option, &option))
|
||||
continue;
|
||||
if (parse_res(option, &option))
|
||||
continue;
|
||||
if (parse_auto(option, &option))
|
||||
continue;
|
||||
if (parse_list(option, &option))
|
||||
continue;
|
||||
|
||||
while (*option && *option++ != ',')
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop)
|
||||
{
|
||||
efi_status_t status;
|
||||
|
||||
efi_graphics_output_protocol_mode_t *mode;
|
||||
efi_graphics_output_mode_info_t *info;
|
||||
unsigned long info_size;
|
||||
|
||||
u32 max_mode, cur_mode;
|
||||
int pf;
|
||||
|
||||
mode = efi_table_attr(gop, mode);
|
||||
|
||||
cur_mode = efi_table_attr(mode, mode);
|
||||
if (cmdline.mode == cur_mode)
|
||||
return cur_mode;
|
||||
|
||||
max_mode = efi_table_attr(mode, max_mode);
|
||||
if (cmdline.mode >= max_mode) {
|
||||
efi_err("Requested mode is invalid\n");
|
||||
return cur_mode;
|
||||
}
|
||||
|
||||
status = efi_call_proto(gop, query_mode, cmdline.mode,
|
||||
&info_size, &info);
|
||||
if (status != EFI_SUCCESS) {
|
||||
efi_err("Couldn't get mode information\n");
|
||||
return cur_mode;
|
||||
}
|
||||
|
||||
pf = info->pixel_format;
|
||||
|
||||
efi_bs_call(free_pool, info);
|
||||
|
||||
if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) {
|
||||
efi_err("Invalid PixelFormat\n");
|
||||
return cur_mode;
|
||||
}
|
||||
|
||||
return cmdline.mode;
|
||||
}
|
||||
|
||||
static u8 pixel_bpp(int pixel_format, efi_pixel_bitmask_t pixel_info)
|
||||
{
|
||||
if (pixel_format == PIXEL_BIT_MASK) {
|
||||
u32 mask = pixel_info.red_mask | pixel_info.green_mask |
|
||||
pixel_info.blue_mask | pixel_info.reserved_mask;
|
||||
if (!mask)
|
||||
return 0;
|
||||
return __fls(mask) - __ffs(mask) + 1;
|
||||
} else
|
||||
return 32;
|
||||
}
|
||||
|
||||
static u32 choose_mode_res(efi_graphics_output_protocol_t *gop)
|
||||
{
|
||||
efi_status_t status;
|
||||
|
||||
efi_graphics_output_protocol_mode_t *mode;
|
||||
efi_graphics_output_mode_info_t *info;
|
||||
unsigned long info_size;
|
||||
|
||||
u32 max_mode, cur_mode;
|
||||
int pf;
|
||||
efi_pixel_bitmask_t pi;
|
||||
u32 m, w, h;
|
||||
|
||||
mode = efi_table_attr(gop, mode);
|
||||
|
||||
cur_mode = efi_table_attr(mode, mode);
|
||||
info = efi_table_attr(mode, info);
|
||||
pf = info->pixel_format;
|
||||
pi = info->pixel_information;
|
||||
w = info->horizontal_resolution;
|
||||
h = info->vertical_resolution;
|
||||
|
||||
if (w == cmdline.res.width && h == cmdline.res.height &&
|
||||
(cmdline.res.format < 0 || cmdline.res.format == pf) &&
|
||||
(!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi)))
|
||||
return cur_mode;
|
||||
|
||||
max_mode = efi_table_attr(mode, max_mode);
|
||||
|
||||
for (m = 0; m < max_mode; m++) {
|
||||
if (m == cur_mode)
|
||||
continue;
|
||||
|
||||
status = efi_call_proto(gop, query_mode, m,
|
||||
&info_size, &info);
|
||||
if (status != EFI_SUCCESS)
|
||||
continue;
|
||||
|
||||
pf = info->pixel_format;
|
||||
pi = info->pixel_information;
|
||||
w = info->horizontal_resolution;
|
||||
h = info->vertical_resolution;
|
||||
|
||||
efi_bs_call(free_pool, info);
|
||||
|
||||
if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
|
||||
continue;
|
||||
if (w == cmdline.res.width && h == cmdline.res.height &&
|
||||
(cmdline.res.format < 0 || cmdline.res.format == pf) &&
|
||||
(!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi)))
|
||||
return m;
|
||||
}
|
||||
|
||||
efi_err("Couldn't find requested mode\n");
|
||||
|
||||
return cur_mode;
|
||||
}
|
||||
|
||||
static u32 choose_mode_auto(efi_graphics_output_protocol_t *gop)
|
||||
{
|
||||
efi_status_t status;
|
||||
|
||||
efi_graphics_output_protocol_mode_t *mode;
|
||||
efi_graphics_output_mode_info_t *info;
|
||||
unsigned long info_size;
|
||||
|
||||
u32 max_mode, cur_mode, best_mode, area;
|
||||
u8 depth;
|
||||
int pf;
|
||||
efi_pixel_bitmask_t pi;
|
||||
u32 m, w, h, a;
|
||||
u8 d;
|
||||
|
||||
mode = efi_table_attr(gop, mode);
|
||||
|
||||
cur_mode = efi_table_attr(mode, mode);
|
||||
max_mode = efi_table_attr(mode, max_mode);
|
||||
|
||||
info = efi_table_attr(mode, info);
|
||||
|
||||
pf = info->pixel_format;
|
||||
pi = info->pixel_information;
|
||||
w = info->horizontal_resolution;
|
||||
h = info->vertical_resolution;
|
||||
|
||||
best_mode = cur_mode;
|
||||
area = w * h;
|
||||
depth = pixel_bpp(pf, pi);
|
||||
|
||||
for (m = 0; m < max_mode; m++) {
|
||||
if (m == cur_mode)
|
||||
continue;
|
||||
|
||||
status = efi_call_proto(gop, query_mode, m,
|
||||
&info_size, &info);
|
||||
if (status != EFI_SUCCESS)
|
||||
continue;
|
||||
|
||||
pf = info->pixel_format;
|
||||
pi = info->pixel_information;
|
||||
w = info->horizontal_resolution;
|
||||
h = info->vertical_resolution;
|
||||
|
||||
efi_bs_call(free_pool, info);
|
||||
|
||||
if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
|
||||
continue;
|
||||
a = w * h;
|
||||
if (a < area)
|
||||
continue;
|
||||
d = pixel_bpp(pf, pi);
|
||||
if (a > area || d > depth) {
|
||||
best_mode = m;
|
||||
area = a;
|
||||
depth = d;
|
||||
}
|
||||
}
|
||||
|
||||
*pos = first;
|
||||
*size = len;
|
||||
return best_mode;
|
||||
}
|
||||
|
||||
static u32 choose_mode_list(efi_graphics_output_protocol_t *gop)
|
||||
{
|
||||
efi_status_t status;
|
||||
|
||||
efi_graphics_output_protocol_mode_t *mode;
|
||||
efi_graphics_output_mode_info_t *info;
|
||||
unsigned long info_size;
|
||||
|
||||
u32 max_mode, cur_mode;
|
||||
int pf;
|
||||
efi_pixel_bitmask_t pi;
|
||||
u32 m, w, h;
|
||||
u8 d;
|
||||
const char *dstr;
|
||||
bool valid;
|
||||
efi_input_key_t key;
|
||||
|
||||
mode = efi_table_attr(gop, mode);
|
||||
|
||||
cur_mode = efi_table_attr(mode, mode);
|
||||
max_mode = efi_table_attr(mode, max_mode);
|
||||
|
||||
efi_printk("Available graphics modes are 0-%u\n", max_mode-1);
|
||||
efi_puts(" * = current mode\n"
|
||||
" - = unusable mode\n");
|
||||
for (m = 0; m < max_mode; m++) {
|
||||
status = efi_call_proto(gop, query_mode, m,
|
||||
&info_size, &info);
|
||||
if (status != EFI_SUCCESS)
|
||||
continue;
|
||||
|
||||
pf = info->pixel_format;
|
||||
pi = info->pixel_information;
|
||||
w = info->horizontal_resolution;
|
||||
h = info->vertical_resolution;
|
||||
|
||||
efi_bs_call(free_pool, info);
|
||||
|
||||
valid = !(pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX);
|
||||
d = 0;
|
||||
switch (pf) {
|
||||
case PIXEL_RGB_RESERVED_8BIT_PER_COLOR:
|
||||
dstr = "rgb";
|
||||
break;
|
||||
case PIXEL_BGR_RESERVED_8BIT_PER_COLOR:
|
||||
dstr = "bgr";
|
||||
break;
|
||||
case PIXEL_BIT_MASK:
|
||||
dstr = "";
|
||||
d = pixel_bpp(pf, pi);
|
||||
break;
|
||||
case PIXEL_BLT_ONLY:
|
||||
dstr = "blt";
|
||||
break;
|
||||
default:
|
||||
dstr = "xxx";
|
||||
break;
|
||||
}
|
||||
|
||||
efi_printk("Mode %3u %c%c: Resolution %ux%u-%s%.0hhu\n",
|
||||
m,
|
||||
m == cur_mode ? '*' : ' ',
|
||||
!valid ? '-' : ' ',
|
||||
w, h, dstr, d);
|
||||
}
|
||||
|
||||
efi_puts("\nPress any key to continue (or wait 10 seconds)\n");
|
||||
status = efi_wait_for_key(10 * EFI_USEC_PER_SEC, &key);
|
||||
if (status != EFI_SUCCESS && status != EFI_TIMEOUT) {
|
||||
efi_err("Unable to read key, continuing in 10 seconds\n");
|
||||
efi_bs_call(stall, 10 * EFI_USEC_PER_SEC);
|
||||
}
|
||||
|
||||
return cur_mode;
|
||||
}
|
||||
|
||||
static void set_mode(efi_graphics_output_protocol_t *gop)
|
||||
{
|
||||
efi_graphics_output_protocol_mode_t *mode;
|
||||
u32 cur_mode, new_mode;
|
||||
|
||||
switch (cmdline.option) {
|
||||
case EFI_CMDLINE_MODE_NUM:
|
||||
new_mode = choose_mode_modenum(gop);
|
||||
break;
|
||||
case EFI_CMDLINE_RES:
|
||||
new_mode = choose_mode_res(gop);
|
||||
break;
|
||||
case EFI_CMDLINE_AUTO:
|
||||
new_mode = choose_mode_auto(gop);
|
||||
break;
|
||||
case EFI_CMDLINE_LIST:
|
||||
new_mode = choose_mode_list(gop);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
mode = efi_table_attr(gop, mode);
|
||||
cur_mode = efi_table_attr(mode, mode);
|
||||
|
||||
if (new_mode == cur_mode)
|
||||
return;
|
||||
|
||||
if (efi_call_proto(gop, set_mode, new_mode) != EFI_SUCCESS)
|
||||
efi_err("Failed to set requested mode\n");
|
||||
}
|
||||
|
||||
static void find_bits(u32 mask, u8 *pos, u8 *size)
|
||||
{
|
||||
if (!mask) {
|
||||
*pos = *size = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* UEFI spec guarantees that the set bits are contiguous */
|
||||
*pos = __ffs(mask);
|
||||
*size = __fls(mask) - *pos + 1;
|
||||
}
|
||||
|
||||
static void
|
||||
setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
|
||||
efi_pixel_bitmask_t pixel_info, int pixel_format)
|
||||
{
|
||||
if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
|
||||
si->lfb_depth = 32;
|
||||
si->lfb_linelength = pixels_per_scan_line * 4;
|
||||
si->red_size = 8;
|
||||
si->red_pos = 0;
|
||||
si->green_size = 8;
|
||||
si->green_pos = 8;
|
||||
si->blue_size = 8;
|
||||
si->blue_pos = 16;
|
||||
si->rsvd_size = 8;
|
||||
si->rsvd_pos = 24;
|
||||
} else if (pixel_format == PIXEL_BGR_RESERVED_8BIT_PER_COLOR) {
|
||||
si->lfb_depth = 32;
|
||||
si->lfb_linelength = pixels_per_scan_line * 4;
|
||||
si->red_size = 8;
|
||||
si->red_pos = 16;
|
||||
si->green_size = 8;
|
||||
si->green_pos = 8;
|
||||
si->blue_size = 8;
|
||||
si->blue_pos = 0;
|
||||
si->rsvd_size = 8;
|
||||
si->rsvd_pos = 24;
|
||||
} else if (pixel_format == PIXEL_BIT_MASK) {
|
||||
find_bits(pixel_info.red_mask, &si->red_pos, &si->red_size);
|
||||
find_bits(pixel_info.green_mask, &si->green_pos,
|
||||
&si->green_size);
|
||||
find_bits(pixel_info.blue_mask, &si->blue_pos, &si->blue_size);
|
||||
find_bits(pixel_info.reserved_mask, &si->rsvd_pos,
|
||||
&si->rsvd_size);
|
||||
if (pixel_format == PIXEL_BIT_MASK) {
|
||||
find_bits(pixel_info.red_mask,
|
||||
&si->red_pos, &si->red_size);
|
||||
find_bits(pixel_info.green_mask,
|
||||
&si->green_pos, &si->green_size);
|
||||
find_bits(pixel_info.blue_mask,
|
||||
&si->blue_pos, &si->blue_size);
|
||||
find_bits(pixel_info.reserved_mask,
|
||||
&si->rsvd_pos, &si->rsvd_size);
|
||||
si->lfb_depth = si->red_size + si->green_size +
|
||||
si->blue_size + si->rsvd_size;
|
||||
si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
|
||||
} else {
|
||||
si->lfb_depth = 4;
|
||||
si->lfb_linelength = si->lfb_width / 2;
|
||||
si->red_size = 0;
|
||||
si->red_pos = 0;
|
||||
si->green_size = 0;
|
||||
si->green_pos = 0;
|
||||
si->blue_size = 0;
|
||||
si->blue_pos = 0;
|
||||
si->rsvd_size = 0;
|
||||
si->rsvd_pos = 0;
|
||||
if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
|
||||
si->red_pos = 0;
|
||||
si->blue_pos = 16;
|
||||
} else /* PIXEL_BGR_RESERVED_8BIT_PER_COLOR */ {
|
||||
si->blue_pos = 0;
|
||||
si->red_pos = 16;
|
||||
}
|
||||
|
||||
si->green_pos = 8;
|
||||
si->rsvd_pos = 24;
|
||||
si->red_size = si->green_size =
|
||||
si->blue_size = si->rsvd_size = 8;
|
||||
|
||||
si->lfb_depth = 32;
|
||||
si->lfb_linelength = pixels_per_scan_line * 4;
|
||||
}
|
||||
}
|
||||
|
||||
static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
|
||||
unsigned long size, void **handles)
|
||||
static efi_graphics_output_protocol_t *
|
||||
find_gop(efi_guid_t *proto, unsigned long size, void **handles)
|
||||
{
|
||||
efi_graphics_output_protocol_t *gop, *first_gop;
|
||||
u16 width, height;
|
||||
u32 pixels_per_scan_line;
|
||||
u32 ext_lfb_base;
|
||||
efi_physical_addr_t fb_base;
|
||||
efi_pixel_bitmask_t pixel_info;
|
||||
int pixel_format;
|
||||
efi_status_t status;
|
||||
efi_graphics_output_protocol_t *first_gop;
|
||||
efi_handle_t h;
|
||||
int i;
|
||||
|
||||
first_gop = NULL;
|
||||
gop = NULL;
|
||||
|
||||
for_each_efi_handle(h, handles, size, i) {
|
||||
efi_status_t status;
|
||||
|
||||
efi_graphics_output_protocol_t *gop;
|
||||
efi_graphics_output_protocol_mode_t *mode;
|
||||
efi_graphics_output_mode_info_t *info = NULL;
|
||||
efi_graphics_output_mode_info_t *info;
|
||||
|
||||
efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
|
||||
bool conout_found = false;
|
||||
void *dummy = NULL;
|
||||
efi_physical_addr_t current_fb_base;
|
||||
|
||||
status = efi_bs_call(handle_protocol, h, proto, (void **)&gop);
|
||||
if (status != EFI_SUCCESS)
|
||||
continue;
|
||||
|
||||
status = efi_bs_call(handle_protocol, h, &conout_proto, &dummy);
|
||||
if (status == EFI_SUCCESS)
|
||||
conout_found = true;
|
||||
|
||||
mode = efi_table_attr(gop, mode);
|
||||
info = efi_table_attr(mode, info);
|
||||
current_fb_base = efi_table_attr(mode, frame_buffer_base);
|
||||
if (info->pixel_format == PIXEL_BLT_ONLY ||
|
||||
info->pixel_format >= PIXEL_FORMAT_MAX)
|
||||
continue;
|
||||
|
||||
if ((!first_gop || conout_found) &&
|
||||
info->pixel_format != PIXEL_BLT_ONLY) {
|
||||
/*
|
||||
* Systems that use the UEFI Console Splitter may
|
||||
* provide multiple GOP devices, not all of which are
|
||||
* backed by real hardware. The workaround is to search
|
||||
* for a GOP implementing the ConOut protocol, and if
|
||||
* one isn't found, to just fall back to the first GOP.
|
||||
*/
|
||||
width = info->horizontal_resolution;
|
||||
height = info->vertical_resolution;
|
||||
pixel_format = info->pixel_format;
|
||||
pixel_info = info->pixel_information;
|
||||
pixels_per_scan_line = info->pixels_per_scan_line;
|
||||
fb_base = current_fb_base;
|
||||
/*
|
||||
* Systems that use the UEFI Console Splitter may
|
||||
* provide multiple GOP devices, not all of which are
|
||||
* backed by real hardware. The workaround is to search
|
||||
* for a GOP implementing the ConOut protocol, and if
|
||||
* one isn't found, to just fall back to the first GOP.
|
||||
*
|
||||
* Once we've found a GOP supporting ConOut,
|
||||
* don't bother looking any further.
|
||||
*/
|
||||
status = efi_bs_call(handle_protocol, h, &conout_proto, &dummy);
|
||||
if (status == EFI_SUCCESS)
|
||||
return gop;
|
||||
|
||||
/*
|
||||
* Once we've found a GOP supporting ConOut,
|
||||
* don't bother looking any further.
|
||||
*/
|
||||
if (!first_gop)
|
||||
first_gop = gop;
|
||||
if (conout_found)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return first_gop;
|
||||
}
|
||||
|
||||
static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
|
||||
unsigned long size, void **handles)
|
||||
{
|
||||
efi_graphics_output_protocol_t *gop;
|
||||
efi_graphics_output_protocol_mode_t *mode;
|
||||
efi_graphics_output_mode_info_t *info;
|
||||
|
||||
gop = find_gop(proto, size, handles);
|
||||
|
||||
/* Did we find any GOPs? */
|
||||
if (!first_gop)
|
||||
if (!gop)
|
||||
return EFI_NOT_FOUND;
|
||||
|
||||
/* Change mode if requested */
|
||||
set_mode(gop);
|
||||
|
||||
/* EFI framebuffer */
|
||||
mode = efi_table_attr(gop, mode);
|
||||
info = efi_table_attr(mode, info);
|
||||
|
||||
si->orig_video_isVGA = VIDEO_TYPE_EFI;
|
||||
|
||||
si->lfb_width = width;
|
||||
si->lfb_height = height;
|
||||
si->lfb_base = fb_base;
|
||||
si->lfb_width = info->horizontal_resolution;
|
||||
si->lfb_height = info->vertical_resolution;
|
||||
|
||||
ext_lfb_base = (u64)(unsigned long)fb_base >> 32;
|
||||
if (ext_lfb_base) {
|
||||
efi_set_u64_split(efi_table_attr(mode, frame_buffer_base),
|
||||
&si->lfb_base, &si->ext_lfb_base);
|
||||
if (si->ext_lfb_base)
|
||||
si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
|
||||
si->ext_lfb_base = ext_lfb_base;
|
||||
}
|
||||
|
||||
si->pages = 1;
|
||||
|
||||
setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format);
|
||||
setup_pixel_info(si, info->pixels_per_scan_line,
|
||||
info->pixel_information, info->pixel_format);
|
||||
|
||||
si->lfb_size = si->lfb_linelength * si->lfb_height;
|
||||
|
||||
|
@ -91,120 +91,23 @@ fail:
|
||||
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_physical_addr_t alloc_addr;
|
||||
efi_status_t status;
|
||||
|
||||
size = round_up(size, EFI_ALLOC_ALIGN);
|
||||
if (EFI_ALLOC_ALIGN > EFI_PAGE_SIZE)
|
||||
return efi_allocate_pages_aligned(size, addr, max,
|
||||
EFI_ALLOC_ALIGN);
|
||||
|
||||
alloc_addr = ALIGN_DOWN(max + 1, EFI_ALLOC_ALIGN) - 1;
|
||||
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS,
|
||||
EFI_LOADER_DATA, size / EFI_PAGE_SIZE + slack,
|
||||
EFI_LOADER_DATA, DIV_ROUND_UP(size, EFI_PAGE_SIZE),
|
||||
&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);
|
||||
}
|
||||
*addr = alloc_addr;
|
||||
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 = ↦
|
||||
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
|
||||
@ -227,81 +130,3 @@ void efi_free(unsigned long size, unsigned long addr)
|
||||
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;
|
||||
}
|
||||
|
@ -28,21 +28,21 @@ void efi_pci_disable_bridge_busmaster(void)
|
||||
|
||||
if (status != EFI_BUFFER_TOO_SMALL) {
|
||||
if (status != EFI_SUCCESS && status != EFI_NOT_FOUND)
|
||||
pr_efi_err("Failed to locate PCI I/O handles'\n");
|
||||
efi_err("Failed to locate PCI I/O handles'\n");
|
||||
return;
|
||||
}
|
||||
|
||||
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, pci_handle_size,
|
||||
(void **)&pci_handle);
|
||||
if (status != EFI_SUCCESS) {
|
||||
pr_efi_err("Failed to allocate memory for 'pci_handle'\n");
|
||||
efi_err("Failed to allocate memory for 'pci_handle'\n");
|
||||
return;
|
||||
}
|
||||
|
||||
status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, &pci_proto,
|
||||
NULL, &pci_handle_size, pci_handle);
|
||||
if (status != EFI_SUCCESS) {
|
||||
pr_efi_err("Failed to locate PCI I/O handles'\n");
|
||||
efi_err("Failed to locate PCI I/O handles'\n");
|
||||
goto free_handle;
|
||||
}
|
||||
|
||||
@ -69,7 +69,7 @@ void efi_pci_disable_bridge_busmaster(void)
|
||||
* access to the framebuffer. Drivers for true PCIe graphics
|
||||
* controllers that are behind a PCIe root port do not use
|
||||
* DMA to implement the GOP framebuffer anyway [although they
|
||||
* may use it in their implentation of Gop->Blt()], and so
|
||||
* may use it in their implementation of Gop->Blt()], and so
|
||||
* disabling DMA in the PCI bridge should not interfere with
|
||||
* normal operation of the device.
|
||||
*/
|
||||
@ -106,7 +106,7 @@ void efi_pci_disable_bridge_busmaster(void)
|
||||
status = efi_call_proto(pci, pci.write, EfiPciIoWidthUint16,
|
||||
PCI_COMMAND, 1, &command);
|
||||
if (status != EFI_SUCCESS)
|
||||
pr_efi_err("Failed to disable PCI busmastering\n");
|
||||
efi_err("Failed to disable PCI busmastering\n");
|
||||
}
|
||||
|
||||
free_handle:
|
||||
|
@ -74,6 +74,8 @@ efi_status_t efi_random_alloc(unsigned long size,
|
||||
if (align < EFI_ALLOC_ALIGN)
|
||||
align = EFI_ALLOC_ALIGN;
|
||||
|
||||
size = round_up(size, 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;
|
||||
@ -85,7 +87,7 @@ efi_status_t efi_random_alloc(unsigned long size,
|
||||
}
|
||||
|
||||
/* find a random number between 0 and total_slots */
|
||||
target_slot = (total_slots * (u16)random_seed) >> 16;
|
||||
target_slot = (total_slots * (u64)(random_seed & U32_MAX)) >> 32;
|
||||
|
||||
/*
|
||||
* target_slot is now a value in the range [0, total_slots), and so
|
||||
@ -109,7 +111,7 @@ efi_status_t efi_random_alloc(unsigned long size,
|
||||
}
|
||||
|
||||
target = round_up(md->phys_addr, align) + target_slot * align;
|
||||
pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
|
||||
pages = size / EFI_PAGE_SIZE;
|
||||
|
||||
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
|
||||
EFI_LOADER_DATA, pages, &target);
|
||||
|
174
drivers/firmware/efi/libstub/relocate.c
Normal file
174
drivers/firmware/efi/libstub/relocate.c
Normal file
@ -0,0 +1,174 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/efi.h>
|
||||
#include <asm/efi.h>
|
||||
|
||||
#include "efistub.h"
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
static 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 = ↦
|
||||
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_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 preferred 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) {
|
||||
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;
|
||||
}
|
@ -67,10 +67,10 @@ enum efi_secureboot_mode efi_get_secureboot(void)
|
||||
return efi_secureboot_mode_disabled;
|
||||
|
||||
secure_boot_enabled:
|
||||
pr_efi("UEFI Secure Boot is enabled.\n");
|
||||
efi_info("UEFI Secure Boot is enabled.\n");
|
||||
return efi_secureboot_mode_enabled;
|
||||
|
||||
out_efi_err:
|
||||
pr_efi_err("Could not determine UEFI Secure Boot status.\n");
|
||||
efi_err("Could not determine UEFI Secure Boot status.\n");
|
||||
return efi_secureboot_mode_unknown;
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ void efi_retrieve_tpm2_eventlog(void)
|
||||
sizeof(*log_tbl) + log_size, (void **)&log_tbl);
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
efi_printk("Unable to allocate memory for event log\n");
|
||||
efi_err("Unable to allocate memory for event log\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
564
drivers/firmware/efi/libstub/vsprintf.c
Normal file
564
drivers/firmware/efi/libstub/vsprintf.c
Normal file
@ -0,0 +1,564 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* -*- linux-c -*- ------------------------------------------------------- *
|
||||
*
|
||||
* Copyright (C) 1991, 1992 Linus Torvalds
|
||||
* Copyright 2007 rPath, Inc. - All Rights Reserved
|
||||
*
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Oh, it's a waste of space, but oh-so-yummy for debugging.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
static
|
||||
int skip_atoi(const char **s)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (isdigit(**s))
|
||||
i = i * 10 + *((*s)++) - '0';
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
* put_dec_full4 handles numbers in the range 0 <= r < 10000.
|
||||
* The multiplier 0xccd is round(2^15/10), and the approximation
|
||||
* r/10 == (r * 0xccd) >> 15 is exact for all r < 16389.
|
||||
*/
|
||||
static
|
||||
void put_dec_full4(char *end, unsigned int r)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
unsigned int q = (r * 0xccd) >> 15;
|
||||
*--end = '0' + (r - q * 10);
|
||||
r = q;
|
||||
}
|
||||
*--end = '0' + r;
|
||||
}
|
||||
|
||||
/* put_dec is copied from lib/vsprintf.c with small modifications */
|
||||
|
||||
/*
|
||||
* Call put_dec_full4 on x % 10000, return x / 10000.
|
||||
* The approximation x/10000 == (x * 0x346DC5D7) >> 43
|
||||
* holds for all x < 1,128,869,999. The largest value this
|
||||
* helper will ever be asked to convert is 1,125,520,955.
|
||||
* (second call in the put_dec code, assuming n is all-ones).
|
||||
*/
|
||||
static
|
||||
unsigned int put_dec_helper4(char *end, unsigned int x)
|
||||
{
|
||||
unsigned int q = (x * 0x346DC5D7ULL) >> 43;
|
||||
|
||||
put_dec_full4(end, x - q * 10000);
|
||||
return q;
|
||||
}
|
||||
|
||||
/* Based on code by Douglas W. Jones found at
|
||||
* <http://www.cs.uiowa.edu/~jones/bcd/decimal.html#sixtyfour>
|
||||
* (with permission from the author).
|
||||
* Performs no 64-bit division and hence should be fast on 32-bit machines.
|
||||
*/
|
||||
static
|
||||
char *put_dec(char *end, unsigned long long n)
|
||||
{
|
||||
unsigned int d3, d2, d1, q, h;
|
||||
char *p = end;
|
||||
|
||||
d1 = ((unsigned int)n >> 16); /* implicit "& 0xffff" */
|
||||
h = (n >> 32);
|
||||
d2 = (h ) & 0xffff;
|
||||
d3 = (h >> 16); /* implicit "& 0xffff" */
|
||||
|
||||
/* n = 2^48 d3 + 2^32 d2 + 2^16 d1 + d0
|
||||
= 281_4749_7671_0656 d3 + 42_9496_7296 d2 + 6_5536 d1 + d0 */
|
||||
q = 656 * d3 + 7296 * d2 + 5536 * d1 + ((unsigned int)n & 0xffff);
|
||||
q = put_dec_helper4(p, q);
|
||||
p -= 4;
|
||||
|
||||
q += 7671 * d3 + 9496 * d2 + 6 * d1;
|
||||
q = put_dec_helper4(p, q);
|
||||
p -= 4;
|
||||
|
||||
q += 4749 * d3 + 42 * d2;
|
||||
q = put_dec_helper4(p, q);
|
||||
p -= 4;
|
||||
|
||||
q += 281 * d3;
|
||||
q = put_dec_helper4(p, q);
|
||||
p -= 4;
|
||||
|
||||
put_dec_full4(p, q);
|
||||
p -= 4;
|
||||
|
||||
/* strip off the extra 0's we printed */
|
||||
while (p < end && *p == '0')
|
||||
++p;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static
|
||||
char *number(char *end, unsigned long long num, int base, char locase)
|
||||
{
|
||||
/*
|
||||
* locase = 0 or 0x20. ORing digits or letters with 'locase'
|
||||
* produces same digits or (maybe lowercased) letters
|
||||
*/
|
||||
|
||||
/* we are called with base 8, 10 or 16, only, thus don't need "G..." */
|
||||
static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */
|
||||
|
||||
switch (base) {
|
||||
case 10:
|
||||
if (num != 0)
|
||||
end = put_dec(end, num);
|
||||
break;
|
||||
case 8:
|
||||
for (; num != 0; num >>= 3)
|
||||
*--end = '0' + (num & 07);
|
||||
break;
|
||||
case 16:
|
||||
for (; num != 0; num >>= 4)
|
||||
*--end = digits[num & 0xf] | locase;
|
||||
break;
|
||||
default:
|
||||
unreachable();
|
||||
};
|
||||
|
||||
return end;
|
||||
}
|
||||
|
||||
#define ZEROPAD 1 /* pad with zero */
|
||||
#define SIGN 2 /* unsigned/signed long */
|
||||
#define PLUS 4 /* show plus */
|
||||
#define SPACE 8 /* space if plus */
|
||||
#define LEFT 16 /* left justified */
|
||||
#define SMALL 32 /* Must be 32 == 0x20 */
|
||||
#define SPECIAL 64 /* 0x */
|
||||
#define WIDE 128 /* UTF-16 string */
|
||||
|
||||
static
|
||||
int get_flags(const char **fmt)
|
||||
{
|
||||
int flags = 0;
|
||||
|
||||
do {
|
||||
switch (**fmt) {
|
||||
case '-':
|
||||
flags |= LEFT;
|
||||
break;
|
||||
case '+':
|
||||
flags |= PLUS;
|
||||
break;
|
||||
case ' ':
|
||||
flags |= SPACE;
|
||||
break;
|
||||
case '#':
|
||||
flags |= SPECIAL;
|
||||
break;
|
||||
case '0':
|
||||
flags |= ZEROPAD;
|
||||
break;
|
||||
default:
|
||||
return flags;
|
||||
}
|
||||
++(*fmt);
|
||||
} while (1);
|
||||
}
|
||||
|
||||
static
|
||||
int get_int(const char **fmt, va_list *ap)
|
||||
{
|
||||
if (isdigit(**fmt))
|
||||
return skip_atoi(fmt);
|
||||
if (**fmt == '*') {
|
||||
++(*fmt);
|
||||
/* it's the next argument */
|
||||
return va_arg(*ap, int);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
unsigned long long get_number(int sign, int qualifier, va_list *ap)
|
||||
{
|
||||
if (sign) {
|
||||
switch (qualifier) {
|
||||
case 'L':
|
||||
return va_arg(*ap, long long);
|
||||
case 'l':
|
||||
return va_arg(*ap, long);
|
||||
case 'h':
|
||||
return (short)va_arg(*ap, int);
|
||||
case 'H':
|
||||
return (signed char)va_arg(*ap, int);
|
||||
default:
|
||||
return va_arg(*ap, int);
|
||||
};
|
||||
} else {
|
||||
switch (qualifier) {
|
||||
case 'L':
|
||||
return va_arg(*ap, unsigned long long);
|
||||
case 'l':
|
||||
return va_arg(*ap, unsigned long);
|
||||
case 'h':
|
||||
return (unsigned short)va_arg(*ap, int);
|
||||
case 'H':
|
||||
return (unsigned char)va_arg(*ap, int);
|
||||
default:
|
||||
return va_arg(*ap, unsigned int);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
char get_sign(long long *num, int flags)
|
||||
{
|
||||
if (!(flags & SIGN))
|
||||
return 0;
|
||||
if (*num < 0) {
|
||||
*num = -(*num);
|
||||
return '-';
|
||||
}
|
||||
if (flags & PLUS)
|
||||
return '+';
|
||||
if (flags & SPACE)
|
||||
return ' ';
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
size_t utf16s_utf8nlen(const u16 *s16, size_t maxlen)
|
||||
{
|
||||
size_t len, clen;
|
||||
|
||||
for (len = 0; len < maxlen && *s16; len += clen) {
|
||||
u16 c0 = *s16++;
|
||||
|
||||
/* First, get the length for a BMP character */
|
||||
clen = 1 + (c0 >= 0x80) + (c0 >= 0x800);
|
||||
if (len + clen > maxlen)
|
||||
break;
|
||||
/*
|
||||
* If this is a high surrogate, and we're already at maxlen, we
|
||||
* can't include the character if it's a valid surrogate pair.
|
||||
* Avoid accessing one extra word just to check if it's valid
|
||||
* or not.
|
||||
*/
|
||||
if ((c0 & 0xfc00) == 0xd800) {
|
||||
if (len + clen == maxlen)
|
||||
break;
|
||||
if ((*s16 & 0xfc00) == 0xdc00) {
|
||||
++s16;
|
||||
++clen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static
|
||||
u32 utf16_to_utf32(const u16 **s16)
|
||||
{
|
||||
u16 c0, c1;
|
||||
|
||||
c0 = *(*s16)++;
|
||||
/* not a surrogate */
|
||||
if ((c0 & 0xf800) != 0xd800)
|
||||
return c0;
|
||||
/* invalid: low surrogate instead of high */
|
||||
if (c0 & 0x0400)
|
||||
return 0xfffd;
|
||||
c1 = **s16;
|
||||
/* invalid: missing low surrogate */
|
||||
if ((c1 & 0xfc00) != 0xdc00)
|
||||
return 0xfffd;
|
||||
/* valid surrogate pair */
|
||||
++(*s16);
|
||||
return (0x10000 - (0xd800 << 10) - 0xdc00) + (c0 << 10) + c1;
|
||||
}
|
||||
|
||||
#define PUTC(c) \
|
||||
do { \
|
||||
if (pos < size) \
|
||||
buf[pos] = (c); \
|
||||
++pos; \
|
||||
} while (0);
|
||||
|
||||
int vsnprintf(char *buf, size_t size, const char *fmt, va_list ap)
|
||||
{
|
||||
/* The maximum space required is to print a 64-bit number in octal */
|
||||
char tmp[(sizeof(unsigned long long) * 8 + 2) / 3];
|
||||
char *tmp_end = &tmp[ARRAY_SIZE(tmp)];
|
||||
long long num;
|
||||
int base;
|
||||
const char *s;
|
||||
size_t len, pos;
|
||||
char sign;
|
||||
|
||||
int flags; /* flags to number() */
|
||||
|
||||
int field_width; /* width of output field */
|
||||
int precision; /* min. # of digits for integers; max
|
||||
number of chars for from string */
|
||||
int qualifier; /* 'h', 'hh', 'l' or 'll' for integer fields */
|
||||
|
||||
va_list args;
|
||||
|
||||
/*
|
||||
* We want to pass our input va_list to helper functions by reference,
|
||||
* but there's an annoying edge case. If va_list was originally passed
|
||||
* to us by value, we could just pass &ap down to the helpers. This is
|
||||
* the case on, for example, X86_32.
|
||||
* However, on X86_64 (and possibly others), va_list is actually a
|
||||
* size-1 array containing a structure. Our function parameter ap has
|
||||
* decayed from T[1] to T*, and &ap has type T** rather than T(*)[1],
|
||||
* which is what will be expected by a function taking a va_list *
|
||||
* parameter.
|
||||
* One standard way to solve this mess is by creating a copy in a local
|
||||
* variable of type va_list and then passing a pointer to that local
|
||||
* copy instead, which is what we do here.
|
||||
*/
|
||||
va_copy(args, ap);
|
||||
|
||||
for (pos = 0; *fmt; ++fmt) {
|
||||
if (*fmt != '%' || *++fmt == '%') {
|
||||
PUTC(*fmt);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* process flags */
|
||||
flags = get_flags(&fmt);
|
||||
|
||||
/* get field width */
|
||||
field_width = get_int(&fmt, &args);
|
||||
if (field_width < 0) {
|
||||
field_width = -field_width;
|
||||
flags |= LEFT;
|
||||
}
|
||||
|
||||
if (flags & LEFT)
|
||||
flags &= ~ZEROPAD;
|
||||
|
||||
/* get the precision */
|
||||
precision = -1;
|
||||
if (*fmt == '.') {
|
||||
++fmt;
|
||||
precision = get_int(&fmt, &args);
|
||||
if (precision >= 0)
|
||||
flags &= ~ZEROPAD;
|
||||
}
|
||||
|
||||
/* get the conversion qualifier */
|
||||
qualifier = -1;
|
||||
if (*fmt == 'h' || *fmt == 'l') {
|
||||
qualifier = *fmt;
|
||||
++fmt;
|
||||
if (qualifier == *fmt) {
|
||||
qualifier -= 'a'-'A';
|
||||
++fmt;
|
||||
}
|
||||
}
|
||||
|
||||
sign = 0;
|
||||
|
||||
switch (*fmt) {
|
||||
case 'c':
|
||||
flags &= LEFT;
|
||||
s = tmp;
|
||||
if (qualifier == 'l') {
|
||||
((u16 *)tmp)[0] = (u16)va_arg(args, unsigned int);
|
||||
((u16 *)tmp)[1] = L'\0';
|
||||
precision = INT_MAX;
|
||||
goto wstring;
|
||||
} else {
|
||||
tmp[0] = (unsigned char)va_arg(args, int);
|
||||
precision = len = 1;
|
||||
}
|
||||
goto output;
|
||||
|
||||
case 's':
|
||||
flags &= LEFT;
|
||||
if (precision < 0)
|
||||
precision = INT_MAX;
|
||||
s = va_arg(args, void *);
|
||||
if (!s)
|
||||
s = precision < 6 ? "" : "(null)";
|
||||
else if (qualifier == 'l') {
|
||||
wstring:
|
||||
flags |= WIDE;
|
||||
precision = len = utf16s_utf8nlen((const u16 *)s, precision);
|
||||
goto output;
|
||||
}
|
||||
precision = len = strnlen(s, precision);
|
||||
goto output;
|
||||
|
||||
/* integer number formats - set up the flags and "break" */
|
||||
case 'o':
|
||||
base = 8;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
if (precision < 0)
|
||||
precision = 2 * sizeof(void *);
|
||||
fallthrough;
|
||||
case 'x':
|
||||
flags |= SMALL;
|
||||
fallthrough;
|
||||
case 'X':
|
||||
base = 16;
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
case 'i':
|
||||
flags |= SIGN;
|
||||
fallthrough;
|
||||
case 'u':
|
||||
flags &= ~SPECIAL;
|
||||
base = 10;
|
||||
break;
|
||||
|
||||
default:
|
||||
/*
|
||||
* Bail out if the conversion specifier is invalid.
|
||||
* There's probably a typo in the format string and the
|
||||
* remaining specifiers are unlikely to match up with
|
||||
* the arguments.
|
||||
*/
|
||||
goto fail;
|
||||
}
|
||||
if (*fmt == 'p') {
|
||||
num = (unsigned long)va_arg(args, void *);
|
||||
} else {
|
||||
num = get_number(flags & SIGN, qualifier, &args);
|
||||
}
|
||||
|
||||
sign = get_sign(&num, flags);
|
||||
if (sign)
|
||||
--field_width;
|
||||
|
||||
s = number(tmp_end, num, base, flags & SMALL);
|
||||
len = tmp_end - s;
|
||||
/* default precision is 1 */
|
||||
if (precision < 0)
|
||||
precision = 1;
|
||||
/* precision is minimum number of digits to print */
|
||||
if (precision < len)
|
||||
precision = len;
|
||||
if (flags & SPECIAL) {
|
||||
/*
|
||||
* For octal, a leading 0 is printed only if necessary,
|
||||
* i.e. if it's not already there because of the
|
||||
* precision.
|
||||
*/
|
||||
if (base == 8 && precision == len)
|
||||
++precision;
|
||||
/*
|
||||
* For hexadecimal, the leading 0x is skipped if the
|
||||
* output is empty, i.e. both the number and the
|
||||
* precision are 0.
|
||||
*/
|
||||
if (base == 16 && precision > 0)
|
||||
field_width -= 2;
|
||||
else
|
||||
flags &= ~SPECIAL;
|
||||
}
|
||||
/*
|
||||
* For zero padding, increase the precision to fill the field
|
||||
* width.
|
||||
*/
|
||||
if ((flags & ZEROPAD) && field_width > precision)
|
||||
precision = field_width;
|
||||
|
||||
output:
|
||||
/* Calculate the padding necessary */
|
||||
field_width -= precision;
|
||||
/* Leading padding with ' ' */
|
||||
if (!(flags & LEFT))
|
||||
while (field_width-- > 0)
|
||||
PUTC(' ');
|
||||
/* sign */
|
||||
if (sign)
|
||||
PUTC(sign);
|
||||
/* 0x/0X for hexadecimal */
|
||||
if (flags & SPECIAL) {
|
||||
PUTC('0');
|
||||
PUTC( 'X' | (flags & SMALL));
|
||||
}
|
||||
/* Zero padding and excess precision */
|
||||
while (precision-- > len)
|
||||
PUTC('0');
|
||||
/* Actual output */
|
||||
if (flags & WIDE) {
|
||||
const u16 *ws = (const u16 *)s;
|
||||
|
||||
while (len-- > 0) {
|
||||
u32 c32 = utf16_to_utf32(&ws);
|
||||
u8 *s8;
|
||||
size_t clen;
|
||||
|
||||
if (c32 < 0x80) {
|
||||
PUTC(c32);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Number of trailing octets */
|
||||
clen = 1 + (c32 >= 0x800) + (c32 >= 0x10000);
|
||||
|
||||
len -= clen;
|
||||
s8 = (u8 *)&buf[pos];
|
||||
|
||||
/* Avoid writing partial character */
|
||||
PUTC('\0');
|
||||
pos += clen;
|
||||
if (pos >= size)
|
||||
continue;
|
||||
|
||||
/* Set high bits of leading octet */
|
||||
*s8 = (0xf00 >> 1) >> clen;
|
||||
/* Write trailing octets in reverse order */
|
||||
for (s8 += clen; clen; --clen, c32 >>= 6)
|
||||
*s8-- = 0x80 | (c32 & 0x3f);
|
||||
/* Set low bits of leading octet */
|
||||
*s8 |= c32;
|
||||
}
|
||||
} else {
|
||||
while (len-- > 0)
|
||||
PUTC(*s++);
|
||||
}
|
||||
/* Trailing padding with ' ' */
|
||||
while (field_width-- > 0)
|
||||
PUTC(' ');
|
||||
}
|
||||
fail:
|
||||
va_end(args);
|
||||
|
||||
if (size)
|
||||
buf[min(pos, size-1)] = '\0';
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
int snprintf(char *buf, size_t size, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int i;
|
||||
|
||||
va_start(args, fmt);
|
||||
i = vsnprintf(buf, size, fmt, args);
|
||||
va_end(args);
|
||||
return i;
|
||||
}
|
@ -20,21 +20,9 @@
|
||||
/* 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 __efistub_global;
|
||||
extern const bool efi_is64;
|
||||
const efi_system_table_t *efi_system_table;
|
||||
extern u32 image_offset;
|
||||
|
||||
__pure efi_system_table_t *efi_system_table(void)
|
||||
{
|
||||
return sys_table;
|
||||
}
|
||||
|
||||
__attribute_const__ bool efi_is_64bit(void)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_EFI_MIXED))
|
||||
return efi_is64;
|
||||
return IS_ENABLED(CONFIG_X86_64);
|
||||
}
|
||||
static efi_loaded_image_t *image = NULL;
|
||||
|
||||
static efi_status_t
|
||||
preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
|
||||
@ -62,7 +50,7 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
|
||||
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, size,
|
||||
(void **)&rom);
|
||||
if (status != EFI_SUCCESS) {
|
||||
efi_printk("Failed to allocate memory for 'rom'\n");
|
||||
efi_err("Failed to allocate memory for 'rom'\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -78,7 +66,7 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
|
||||
PCI_VENDOR_ID, 1, &rom->vendor);
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
efi_printk("Failed to read rom->vendor\n");
|
||||
efi_err("Failed to read rom->vendor\n");
|
||||
goto free_struct;
|
||||
}
|
||||
|
||||
@ -86,7 +74,7 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
|
||||
PCI_DEVICE_ID, 1, &rom->devid);
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
efi_printk("Failed to read rom->devid\n");
|
||||
efi_err("Failed to read rom->devid\n");
|
||||
goto free_struct;
|
||||
}
|
||||
|
||||
@ -131,7 +119,7 @@ static void setup_efi_pci(struct boot_params *params)
|
||||
(void **)&pci_handle);
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
efi_printk("Failed to allocate memory for 'pci_handle'\n");
|
||||
efi_err("Failed to allocate memory for 'pci_handle'\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -185,7 +173,7 @@ static void retrieve_apple_device_properties(struct boot_params *boot_params)
|
||||
return;
|
||||
|
||||
if (efi_table_attr(p, version) != 0x10000) {
|
||||
efi_printk("Unsupported properties proto version\n");
|
||||
efi_err("Unsupported properties proto version\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -198,7 +186,7 @@ static void retrieve_apple_device_properties(struct boot_params *boot_params)
|
||||
size + sizeof(struct setup_data),
|
||||
(void **)&new);
|
||||
if (status != EFI_SUCCESS) {
|
||||
efi_printk("Failed to allocate memory for 'properties'\n");
|
||||
efi_err("Failed to allocate memory for 'properties'\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -227,7 +215,7 @@ static const efi_char16_t apple[] = L"Apple";
|
||||
static void setup_quirks(struct boot_params *boot_params)
|
||||
{
|
||||
efi_char16_t *fw_vendor = (efi_char16_t *)(unsigned long)
|
||||
efi_table_attr(efi_system_table(), fw_vendor);
|
||||
efi_table_attr(efi_system_table, fw_vendor);
|
||||
|
||||
if (!memcmp(fw_vendor, apple, sizeof(apple))) {
|
||||
if (IS_ENABLED(CONFIG_APPLE_PROPERTIES))
|
||||
@ -368,7 +356,6 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
|
||||
{
|
||||
struct boot_params *boot_params;
|
||||
struct setup_header *hdr;
|
||||
efi_loaded_image_t *image;
|
||||
void *image_base;
|
||||
efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
|
||||
int options_size = 0;
|
||||
@ -377,28 +364,29 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
|
||||
unsigned long ramdisk_addr;
|
||||
unsigned long ramdisk_size;
|
||||
|
||||
sys_table = sys_table_arg;
|
||||
efi_system_table = sys_table_arg;
|
||||
|
||||
/* Check if we were booted by the EFI firmware */
|
||||
if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
|
||||
if (efi_system_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
|
||||
efi_exit(handle, EFI_INVALID_PARAMETER);
|
||||
|
||||
status = efi_bs_call(handle_protocol, handle, &proto, (void **)&image);
|
||||
if (status != EFI_SUCCESS) {
|
||||
efi_printk("Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
|
||||
efi_err("Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
|
||||
efi_exit(handle, status);
|
||||
}
|
||||
|
||||
image_base = efi_table_attr(image, image_base);
|
||||
image_offset = (void *)startup_32 - image_base;
|
||||
|
||||
status = efi_allocate_pages(0x4000, (unsigned long *)&boot_params, ULONG_MAX);
|
||||
status = efi_allocate_pages(sizeof(struct boot_params),
|
||||
(unsigned long *)&boot_params, ULONG_MAX);
|
||||
if (status != EFI_SUCCESS) {
|
||||
efi_printk("Failed to allocate lowmem for boot params\n");
|
||||
efi_err("Failed to allocate lowmem for boot params\n");
|
||||
efi_exit(handle, status);
|
||||
}
|
||||
|
||||
memset(boot_params, 0x0, 0x4000);
|
||||
memset(boot_params, 0x0, sizeof(struct boot_params));
|
||||
|
||||
hdr = &boot_params->hdr;
|
||||
|
||||
@ -416,43 +404,21 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
|
||||
hdr->type_of_loader = 0x21;
|
||||
|
||||
/* Convert unicode cmdline to ascii */
|
||||
cmdline_ptr = efi_convert_cmdline(image, &options_size, ULONG_MAX);
|
||||
cmdline_ptr = efi_convert_cmdline(image, &options_size);
|
||||
if (!cmdline_ptr)
|
||||
goto fail;
|
||||
|
||||
hdr->cmd_line_ptr = (unsigned long)cmdline_ptr;
|
||||
/* Fill in upper bits of command line address, NOP on 32 bit */
|
||||
boot_params->ext_cmd_line_ptr = (u64)(unsigned long)cmdline_ptr >> 32;
|
||||
efi_set_u64_split((unsigned long)cmdline_ptr,
|
||||
&hdr->cmd_line_ptr, &boot_params->ext_cmd_line_ptr);
|
||||
|
||||
hdr->ramdisk_image = 0;
|
||||
hdr->ramdisk_size = 0;
|
||||
|
||||
if (efi_is_native()) {
|
||||
status = efi_parse_options(cmdline_ptr);
|
||||
if (status != EFI_SUCCESS)
|
||||
goto fail2;
|
||||
|
||||
if (!noinitrd()) {
|
||||
status = efi_load_initrd(image, &ramdisk_addr,
|
||||
&ramdisk_size,
|
||||
hdr->initrd_addr_max,
|
||||
ULONG_MAX);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
efi_stub_entry(handle, sys_table, boot_params);
|
||||
efi_stub_entry(handle, sys_table_arg, boot_params);
|
||||
/* not reached */
|
||||
|
||||
fail2:
|
||||
efi_free(options_size, (unsigned long)cmdline_ptr);
|
||||
fail:
|
||||
efi_free(0x4000, (unsigned long)boot_params);
|
||||
efi_free(sizeof(struct boot_params), (unsigned long)boot_params);
|
||||
|
||||
efi_exit(handle, status);
|
||||
}
|
||||
@ -645,17 +611,14 @@ static efi_status_t exit_boot_func(struct efi_boot_memmap *map,
|
||||
: EFI32_LOADER_SIGNATURE;
|
||||
memcpy(&p->efi->efi_loader_signature, signature, sizeof(__u32));
|
||||
|
||||
p->efi->efi_systab = (unsigned long)efi_system_table();
|
||||
efi_set_u64_split((unsigned long)efi_system_table,
|
||||
&p->efi->efi_systab, &p->efi->efi_systab_hi);
|
||||
p->efi->efi_memdesc_size = *map->desc_size;
|
||||
p->efi->efi_memdesc_version = *map->desc_ver;
|
||||
p->efi->efi_memmap = (unsigned long)*map->map;
|
||||
efi_set_u64_split((unsigned long)*map->map,
|
||||
&p->efi->efi_memmap, &p->efi->efi_memmap_hi);
|
||||
p->efi->efi_memmap_size = *map->map_size;
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
p->efi->efi_systab_hi = (unsigned long)efi_system_table() >> 32;
|
||||
p->efi->efi_memmap_hi = (unsigned long)*map->map >> 32;
|
||||
#endif
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
@ -711,12 +674,11 @@ unsigned long efi_main(efi_handle_t handle,
|
||||
unsigned long buffer_start, buffer_end;
|
||||
struct setup_header *hdr = &boot_params->hdr;
|
||||
efi_status_t status;
|
||||
unsigned long cmdline_paddr;
|
||||
|
||||
sys_table = sys_table_arg;
|
||||
efi_system_table = sys_table_arg;
|
||||
|
||||
/* Check if we were booted by the EFI firmware */
|
||||
if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
|
||||
if (efi_system_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
|
||||
efi_exit(handle, EFI_INVALID_PARAMETER);
|
||||
|
||||
/*
|
||||
@ -759,7 +721,7 @@ unsigned long efi_main(efi_handle_t handle,
|
||||
hdr->kernel_alignment,
|
||||
LOAD_PHYSICAL_ADDR);
|
||||
if (status != EFI_SUCCESS) {
|
||||
efi_printk("efi_relocate_kernel() failed!\n");
|
||||
efi_err("efi_relocate_kernel() failed!\n");
|
||||
goto fail;
|
||||
}
|
||||
/*
|
||||
@ -770,35 +732,48 @@ unsigned long efi_main(efi_handle_t handle,
|
||||
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,
|
||||
* parsing the cmdline multiple times does not have side-effects.
|
||||
*/
|
||||
cmdline_paddr = ((u64)hdr->cmd_line_ptr |
|
||||
((u64)boot_params->ext_cmd_line_ptr << 32));
|
||||
efi_parse_options((char *)cmdline_paddr);
|
||||
#ifdef CONFIG_CMDLINE_BOOL
|
||||
status = efi_parse_options(CONFIG_CMDLINE);
|
||||
if (status != EFI_SUCCESS) {
|
||||
efi_err("Failed to parse options\n");
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
if (!IS_ENABLED(CONFIG_CMDLINE_OVERRIDE)) {
|
||||
unsigned long cmdline_paddr = ((u64)hdr->cmd_line_ptr |
|
||||
((u64)boot_params->ext_cmd_line_ptr << 32));
|
||||
status = efi_parse_options((char *)cmdline_paddr);
|
||||
if (status != EFI_SUCCESS) {
|
||||
efi_err("Failed to parse options\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* At this point, an initrd may already have been loaded by the
|
||||
* bootloader and passed via bootparams. We permit an initrd loaded
|
||||
* from the LINUX_EFI_INITRD_MEDIA_GUID device path to supersede it.
|
||||
*
|
||||
* If the device path is not present, any command-line initrd=
|
||||
* arguments will be processed only if image is not NULL, which will be
|
||||
* the case only if we were loaded via the PE entry point.
|
||||
*/
|
||||
if (!noinitrd()) {
|
||||
if (!efi_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");
|
||||
status = efi_load_initrd(image, &addr, &size,
|
||||
hdr->initrd_addr_max, ULONG_MAX);
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
efi_err("Failed to load initrd!\n");
|
||||
goto fail;
|
||||
}
|
||||
if (size > 0) {
|
||||
efi_set_u64_split(addr, &hdr->ramdisk_image,
|
||||
&boot_params->ext_ramdisk_image);
|
||||
efi_set_u64_split(size, &hdr->ramdisk_size,
|
||||
&boot_params->ext_ramdisk_size);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -823,13 +798,13 @@ unsigned long efi_main(efi_handle_t handle,
|
||||
|
||||
status = exit_boot(boot_params, handle);
|
||||
if (status != EFI_SUCCESS) {
|
||||
efi_printk("exit_boot() failed!\n");
|
||||
efi_err("exit_boot() failed!\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return bzimage_addr;
|
||||
fail:
|
||||
efi_printk("efi_main() failed!\n");
|
||||
efi_err("efi_main() failed!\n");
|
||||
|
||||
efi_exit(handle, status);
|
||||
}
|
||||
|
@ -39,6 +39,7 @@
|
||||
#define EFI_WRITE_PROTECTED ( 8 | (1UL << (BITS_PER_LONG-1)))
|
||||
#define EFI_OUT_OF_RESOURCES ( 9 | (1UL << (BITS_PER_LONG-1)))
|
||||
#define EFI_NOT_FOUND (14 | (1UL << (BITS_PER_LONG-1)))
|
||||
#define EFI_TIMEOUT (18 | (1UL << (BITS_PER_LONG-1)))
|
||||
#define EFI_ABORTED (21 | (1UL << (BITS_PER_LONG-1)))
|
||||
#define EFI_SECURITY_VIOLATION (26 | (1UL << (BITS_PER_LONG-1)))
|
||||
|
||||
@ -379,8 +380,8 @@ typedef union {
|
||||
|
||||
typedef struct {
|
||||
efi_guid_t guid;
|
||||
const char *name;
|
||||
unsigned long *ptr;
|
||||
const char name[16];
|
||||
} efi_config_table_type_t;
|
||||
|
||||
#define EFI_SYSTEM_TABLE_SIGNATURE ((u64)0x5453595320494249ULL)
|
||||
@ -426,6 +427,7 @@ typedef struct {
|
||||
u32 tables;
|
||||
} efi_system_table_32_t;
|
||||
|
||||
typedef union efi_simple_text_input_protocol efi_simple_text_input_protocol_t;
|
||||
typedef union efi_simple_text_output_protocol efi_simple_text_output_protocol_t;
|
||||
|
||||
typedef union {
|
||||
@ -434,7 +436,7 @@ typedef union {
|
||||
unsigned long fw_vendor; /* physical addr of CHAR16 vendor string */
|
||||
u32 fw_revision;
|
||||
unsigned long con_in_handle;
|
||||
unsigned long con_in;
|
||||
efi_simple_text_input_protocol_t *con_in;
|
||||
unsigned long con_out_handle;
|
||||
efi_simple_text_output_protocol_t *con_out;
|
||||
unsigned long stderr_handle;
|
||||
|
Loading…
Reference in New Issue
Block a user