Commit d1343da3 authored by Ingo Molnar's avatar Ingo Molnar

Merge tag 'efi-changes-for-v5.8' of...

Merge tag 'efi-changes-for-v5.8' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi into efi/core

More EFI changes for v5.8:

 - 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
 - Some fixes for issues introduced by the first batch of v5.8 changes
 - Fix a missing prototypes warning
 - Simplify GDT handling in early mixed mode thunking code
 - Some other minor fixes and cleanups

Conflicts:
	drivers/firmware/efi/libstub/efistub.h
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents a5d8e55b 9241dfe7
...@@ -63,4 +63,9 @@ auto ...@@ -63,4 +63,9 @@ auto
with the highest resolution, it will choose one with the highest color with the highest resolution, it will choose one with the highest color
depth. 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> Edgar Hucek <gimli@dark-green.com>
...@@ -60,7 +60,7 @@ optional_header: ...@@ -60,7 +60,7 @@ optional_header:
.long __pecoff_code_size @ SizeOfCode .long __pecoff_code_size @ SizeOfCode
.long __pecoff_data_size @ SizeOfInitializedData .long __pecoff_data_size @ SizeOfInitializedData
.long 0 @ SizeOfUninitializedData .long 0 @ SizeOfUninitializedData
.long efi_entry - start @ AddressOfEntryPoint .long efi_pe_entry - start @ AddressOfEntryPoint
.long start_offset @ BaseOfCode .long start_offset @ BaseOfCode
.long __pecoff_data_start - start @ BaseOfData .long __pecoff_data_start - start @ BaseOfData
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
SYM_CODE_START(efi_enter_kernel) 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 * end up here with device tree address in x1 and the kernel entry
* point stored in x0. Save those values in registers which are * point stored in x0. Save those values in registers which are
* callee preserved. * callee preserved.
......
...@@ -27,7 +27,7 @@ optional_header: ...@@ -27,7 +27,7 @@ optional_header:
.long __initdata_begin - efi_header_end // SizeOfCode .long __initdata_begin - efi_header_end // SizeOfCode
.long __pecoff_data_size // SizeOfInitializedData .long __pecoff_data_size // SizeOfInitializedData
.long 0 // SizeOfUninitializedData .long 0 // SizeOfUninitializedData
.long __efistub_efi_entry - _head // AddressOfEntryPoint .long __efistub_efi_pe_entry - _head // AddressOfEntryPoint
.long efi_header_end - _head // BaseOfCode .long efi_header_end - _head // BaseOfCode
extra_header_fields: extra_header_fields:
......
...@@ -28,8 +28,6 @@ SYM_FUNC_START(__efi64_thunk) ...@@ -28,8 +28,6 @@ SYM_FUNC_START(__efi64_thunk)
push %rbx push %rbx
leaq 1f(%rip), %rbp leaq 1f(%rip), %rbp
leaq efi_gdt64(%rip), %rbx
movl %ebx, 2(%rbx) /* Fixup the gdt base address */
movl %ds, %eax movl %ds, %eax
push %rax push %rax
...@@ -48,7 +46,8 @@ SYM_FUNC_START(__efi64_thunk) ...@@ -48,7 +46,8 @@ SYM_FUNC_START(__efi64_thunk)
movl %r8d, 0xc(%rsp) movl %r8d, 0xc(%rsp)
movl %r9d, 0x10(%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 * Switch to gdt with 32-bit segments. This is the firmware GDT
...@@ -68,8 +67,7 @@ SYM_FUNC_START(__efi64_thunk) ...@@ -68,8 +67,7 @@ SYM_FUNC_START(__efi64_thunk)
pushq %rax pushq %rax
lretq lretq
1: lgdt 0x14(%rsp) 1: addq $32, %rsp
addq $32, %rsp
movq %rdi, %rax movq %rdi, %rax
pop %rbx pop %rbx
...@@ -175,14 +173,3 @@ SYM_DATA_END(efi32_boot_cs) ...@@ -175,14 +173,3 @@ SYM_DATA_END(efi32_boot_cs)
SYM_DATA_START(efi32_boot_ds) SYM_DATA_START(efi32_boot_ds)
.word 0 .word 0
SYM_DATA_END(efi32_boot_ds) 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)
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <asm/nospec-branch.h> #include <asm/nospec-branch.h>
#include <asm/mmu_context.h> #include <asm/mmu_context.h>
#include <linux/build_bug.h> #include <linux/build_bug.h>
#include <linux/kernel.h>
extern unsigned long efi_fw_vendor, efi_config_table; extern unsigned long efi_fw_vendor, efi_config_table;
...@@ -225,13 +226,15 @@ efi_status_t efi_set_virtual_address_map(unsigned long memory_map_size, ...@@ -225,13 +226,15 @@ efi_status_t efi_set_virtual_address_map(unsigned long memory_map_size,
/* arch specific definitions used by the stub code */ /* arch specific definitions used by the stub code */
extern const bool efi_is64; #ifdef CONFIG_EFI_MIXED
#define ARCH_HAS_EFISTUB_WRAPPERS
static inline bool efi_is_64bit(void) static inline bool efi_is_64bit(void)
{ {
if (IS_ENABLED(CONFIG_EFI_MIXED)) extern const bool efi_is64;
return efi_is64;
return IS_ENABLED(CONFIG_X86_64); return efi_is64;
} }
static inline bool efi_is_native(void) static inline bool efi_is_native(void)
...@@ -291,6 +294,15 @@ static inline u32 efi64_convert_status(efi_status_t status) ...@@ -291,6 +294,15 @@ static inline u32 efi64_convert_status(efi_status_t status)
#define __efi64_argmap_allocate_pool(type, size, buffer) \ #define __efi64_argmap_allocate_pool(type, size, buffer) \
((type), (size), efi64_zero_upper(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) \ #define __efi64_argmap_handle_protocol(handle, protocol, interface) \
((handle), (protocol), efi64_zero_upper(interface)) ((handle), (protocol), efi64_zero_upper(interface))
...@@ -356,6 +368,15 @@ static inline u32 efi64_convert_status(efi_status_t status) ...@@ -356,6 +368,15 @@ static inline u32 efi64_convert_status(efi_status_t status)
runtime), \ runtime), \
func, __VA_ARGS__)) 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_reboot_required(void);
extern bool efi_is_table_address(unsigned long phys_addr); extern bool efi_is_table_address(unsigned long phys_addr);
......
...@@ -29,7 +29,7 @@ static efi_system_table_t efi_systab_xen __initdata = { ...@@ -29,7 +29,7 @@ static efi_system_table_t efi_systab_xen __initdata = {
.fw_vendor = EFI_INVALID_TABLE_ADDR, /* Initialized later. */ .fw_vendor = EFI_INVALID_TABLE_ADDR, /* Initialized later. */
.fw_revision = 0, /* Initialized later. */ .fw_revision = 0, /* Initialized later. */
.con_in_handle = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */ .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_handle = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */
.con_out = NULL, /* Not used under Xen. */ .con_out = NULL, /* Not used under Xen. */
.stderr_handle = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */ .stderr_handle = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */
......
...@@ -522,8 +522,10 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var) ...@@ -522,8 +522,10 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var)
ret = kobject_init_and_add(&new_var->kobj, &efivar_ktype, ret = kobject_init_and_add(&new_var->kobj, &efivar_ktype,
NULL, "%s", short_name); NULL, "%s", short_name);
kfree(short_name); kfree(short_name);
if (ret) if (ret) {
kobject_put(&new_var->kobj);
return ret; return ret;
}
kobject_uevent(&new_var->kobj, KOBJ_ADD); kobject_uevent(&new_var->kobj, KOBJ_ADD);
if (efivar_entry_add(new_var, &efivar_sysfs_list)) { if (efivar_entry_add(new_var, &efivar_sysfs_list)) {
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
# #
cflags-$(CONFIG_X86_32) := -march=i386 cflags-$(CONFIG_X86_32) := -march=i386
cflags-$(CONFIG_X86_64) := -mcmodel=small 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 \ -fPIC -fno-strict-aliasing -mno-red-zone \
-mno-mmx -mno-sse -fshort-wchar \ -mno-mmx -mno-sse -fshort-wchar \
-Wno-pointer-sign \ -Wno-pointer-sign \
...@@ -25,11 +25,12 @@ cflags-$(CONFIG_ARM) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ ...@@ -25,11 +25,12 @@ cflags-$(CONFIG_ARM) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \
cflags-$(CONFIG_EFI_GENERIC_STUB) += -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 \ -include $(srctree)/drivers/firmware/efi/libstub/hidden.h \
-D__NO_FORTIFY \ -D__NO_FORTIFY \
$(call cc-option,-ffreestanding) \ $(call cc-option,-ffreestanding) \
$(call cc-option,-fno-stack-protector) \ $(call cc-option,-fno-stack-protector) \
$(call cc-option,-fno-addrsig) \
-D__DISABLE_EXPORTS -D__DISABLE_EXPORTS
GCOV_PROFILE := n GCOV_PROFILE := n
...@@ -43,7 +44,7 @@ KCOV_INSTRUMENT := n ...@@ -43,7 +44,7 @@ KCOV_INSTRUMENT := n
lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \ lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \
file.o mem.o random.o randomalloc.o pci.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 alignedmem.o relocate.o vsprintf.o
# include the stub's generic dependencies from lib/ when building for ARM/arm64 # include the stub's generic dependencies from lib/ when building for ARM/arm64
efi-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
......
...@@ -18,7 +18,7 @@ efi_status_t check_platform_features(void) ...@@ -18,7 +18,7 @@ efi_status_t check_platform_features(void)
/* LPAE kernels need compatible hardware */ /* LPAE kernels need compatible hardware */
block = cpuid_feature_extract(CPUID_EXT_MMFR0, 0); block = cpuid_feature_extract(CPUID_EXT_MMFR0, 0);
if (block < 5) { 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_UNSUPPORTED;
} }
return EFI_SUCCESS; return EFI_SUCCESS;
...@@ -120,7 +120,7 @@ static efi_status_t reserve_kernel_base(unsigned long dram_base, ...@@ -120,7 +120,7 @@ static efi_status_t reserve_kernel_base(unsigned long dram_base,
*/ */
status = efi_get_memory_map(&map); status = efi_get_memory_map(&map);
if (status != EFI_SUCCESS) { 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; return status;
} }
...@@ -162,7 +162,7 @@ static efi_status_t reserve_kernel_base(unsigned long dram_base, ...@@ -162,7 +162,7 @@ static efi_status_t reserve_kernel_base(unsigned long dram_base,
(end - start) / EFI_PAGE_SIZE, (end - start) / EFI_PAGE_SIZE,
&start); &start);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
pr_efi_err("reserve_kernel_base(): alloc failed.\n"); efi_err("reserve_kernel_base(): alloc failed.\n");
goto out; goto out;
} }
break; break;
...@@ -219,7 +219,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, ...@@ -219,7 +219,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
status = reserve_kernel_base(kernel_base, reserve_addr, reserve_size); status = reserve_kernel_base(kernel_base, reserve_addr, reserve_size);
if (status != EFI_SUCCESS) { 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; return status;
} }
...@@ -232,7 +232,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, ...@@ -232,7 +232,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
status = efi_relocate_kernel(image_addr, *image_size, *image_size, status = efi_relocate_kernel(image_addr, *image_size, *image_size,
kernel_base + MAX_UNCOMP_KERNEL_SIZE, 0, 0); kernel_base + MAX_UNCOMP_KERNEL_SIZE, 0, 0);
if (status != EFI_SUCCESS) { 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); efi_free(*reserve_size, *reserve_addr);
*reserve_size = 0; *reserve_size = 0;
return status; return status;
...@@ -244,7 +244,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, ...@@ -244,7 +244,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
* address at which the zImage is loaded. * address at which the zImage is loaded.
*/ */
if (*image_addr + *image_size > dram_base + ZIMAGE_OFFSET_LIMIT) { 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); efi_free(*reserve_size, *reserve_addr);
*reserve_size = 0; *reserve_size = 0;
efi_free(*image_size, *image_addr); efi_free(*image_size, *image_addr);
......
...@@ -26,9 +26,9 @@ efi_status_t check_platform_features(void) ...@@ -26,9 +26,9 @@ efi_status_t check_platform_features(void)
tg = (read_cpuid(ID_AA64MMFR0_EL1) >> ID_AA64MMFR0_TGRAN_SHIFT) & 0xf; tg = (read_cpuid(ID_AA64MMFR0_EL1) >> ID_AA64MMFR0_TGRAN_SHIFT) & 0xf;
if (tg != ID_AA64MMFR0_TGRAN_SUPPORTED) { if (tg != ID_AA64MMFR0_TGRAN_SUPPORTED) {
if (IS_ENABLED(CONFIG_ARM64_64K_PAGES)) 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 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_UNSUPPORTED;
} }
return EFI_SUCCESS; return EFI_SUCCESS;
...@@ -59,18 +59,18 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, ...@@ -59,18 +59,18 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
status = efi_get_random_bytes(sizeof(phys_seed), status = efi_get_random_bytes(sizeof(phys_seed),
(u8 *)&phys_seed); (u8 *)&phys_seed);
if (status == EFI_NOT_FOUND) { 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) { } else if (status != EFI_SUCCESS) {
pr_efi_err("efi_get_random_bytes() failed\n"); efi_err("efi_get_random_bytes() failed\n");
return status; return status;
} }
} else { } else {
pr_efi("KASLR disabled on kernel command line\n"); efi_info("KASLR disabled on kernel command line\n");
} }
} }
if (image->image_base != _text) if (image->image_base != _text)
pr_efi_err("FIRMWARE BUG: efi_loaded_image_t::image_base has bogus value\n"); efi_err("FIRMWARE BUG: efi_loaded_image_t::image_base has bogus value\n");
kernel_size = _edata - _text; kernel_size = _edata - _text;
kernel_memsize = kernel_size + (_end - _edata); kernel_memsize = kernel_size + (_end - _edata);
...@@ -102,7 +102,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, ...@@ -102,7 +102,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
ULONG_MAX, min_kimg_align); ULONG_MAX, min_kimg_align);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
pr_efi_err("Failed to relocate kernel\n"); efi_err("Failed to relocate kernel\n");
*reserve_size = 0; *reserve_size = 0;
return status; return status;
} }
......
...@@ -7,15 +7,21 @@ ...@@ -7,15 +7,21 @@
* Copyright 2011 Intel Corporation; author Matt Fleming * Copyright 2011 Intel Corporation; author Matt Fleming
*/ */
#include <stdarg.h>
#include <linux/ctype.h>
#include <linux/efi.h> #include <linux/efi.h>
#include <linux/kernel.h>
#include <linux/printk.h> /* For CONSOLE_LOGLEVEL_* */
#include <asm/efi.h> #include <asm/efi.h>
#include <asm/setup.h>
#include "efistub.h" #include "efistub.h"
bool efi_nochunk; bool efi_nochunk;
bool efi_nokaslr; bool efi_nokaslr;
bool efi_noinitrd; bool efi_noinitrd;
bool efi_quiet; int efi_loglevel = CONSOLE_LOGLEVEL_DEFAULT;
bool efi_novamap; bool efi_novamap;
static bool efi_nosoftreserve; static bool efi_nosoftreserve;
...@@ -26,21 +32,126 @@ bool __pure __efi_soft_reserve_enabled(void) ...@@ -26,21 +32,126 @@ bool __pure __efi_soft_reserve_enabled(void)
return !efi_nosoftreserve; 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++) { static
efi_char16_t ch[2] = { 0 }; u32 utf8_to_utf32(const u8 **s8)
{
u32 c32;
u8 c0, cx;
size_t clen, i;
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;
}
ch[0] = *s8; void efi_puts(const char *str)
if (*s8 == '\n') { {
efi_char16_t nl[2] = { '\r', 0 }; efi_char16_t buf[128];
efi_char16_printk(nl); 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_char16_printk(ch); efi_puts(printf_buf);
if (printed >= sizeof(printf_buf)) {
efi_puts("[Message truncated]\n");
return -1;
} }
return printed;
} }
/* /*
...@@ -71,7 +182,7 @@ efi_status_t efi_parse_options(char const *cmdline) ...@@ -71,7 +182,7 @@ efi_status_t efi_parse_options(char const *cmdline)
if (!strcmp(param, "nokaslr")) { if (!strcmp(param, "nokaslr")) {
efi_nokaslr = true; efi_nokaslr = true;
} else if (!strcmp(param, "quiet")) { } else if (!strcmp(param, "quiet")) {
efi_quiet = true; efi_loglevel = CONSOLE_LOGLEVEL_QUIET;
} else if (!strcmp(param, "noinitrd")) { } else if (!strcmp(param, "noinitrd")) {
efi_noinitrd = true; efi_noinitrd = true;
} else if (!strcmp(param, "efi") && val) { } else if (!strcmp(param, "efi") && val) {
...@@ -85,6 +196,8 @@ efi_status_t efi_parse_options(char const *cmdline) ...@@ -85,6 +196,8 @@ efi_status_t efi_parse_options(char const *cmdline)
efi_disable_pci_dma = true; efi_disable_pci_dma = true;
if (parse_option_str(val, "no_disable_early_pci_dma")) if (parse_option_str(val, "no_disable_early_pci_dma"))
efi_disable_pci_dma = false; efi_disable_pci_dma = false;
if (parse_option_str(val, "debug"))
efi_loglevel = CONSOLE_LOGLEVEL_DEBUG;
} else if (!strcmp(param, "video") && } else if (!strcmp(param, "video") &&
val && strstarts(val, "efifb:")) { val && strstarts(val, "efifb:")) {
efi_parse_option_graphics(val + strlen("efifb:")); efi_parse_option_graphics(val + strlen("efifb:"));
...@@ -94,98 +207,80 @@ efi_status_t efi_parse_options(char const *cmdline) ...@@ -94,98 +207,80 @@ efi_status_t efi_parse_options(char const *cmdline)
return EFI_SUCCESS; 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. * Convert the unicode UEFI command line to ASCII to pass to kernel.
* Size of memory allocated return in *cmd_line_len. * Size of memory allocated return in *cmd_line_len.
* Returns NULL on error. * Returns NULL on error.
*/ */
char *efi_convert_cmdline(efi_loaded_image_t *image, char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len)
int *cmd_line_len, unsigned long max_addr)
{ {
const u16 *s2; const u16 *s2;
u8 *s1 = NULL;
unsigned long cmdline_addr = 0; 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); const u16 *options = efi_table_attr(image, load_options);
int options_bytes = 0; /* UTF-8 bytes */ int options_bytes = 0, safe_options_bytes = 0; /* UTF-8 bytes */
int options_chars = 0; /* UTF-16 chars */ bool in_quote = false;
efi_status_t status; efi_status_t status;
u16 zero = 0;
if (options) { if (options) {
s2 = options; s2 = options;
while (*s2 && *s2 != '\n' while (options_bytes < COMMAND_LINE_SIZE && options_chars--) {
&& options_chars < load_options_chars) { u16 c = *s2++;
options_bytes += efi_utf8_bytes(*s2++);
options_chars++; 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);
} }
}
if (!options_chars) {
/* No command line options, so return empty string*/
options = &zero;
} }
options_bytes++; /* NUL termination */ 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) if (status != EFI_SUCCESS)
return NULL; return NULL;
s1 = (u8 *)cmdline_addr; snprintf((char *)cmdline_addr, options_bytes, "%.*ls",
s2 = (const u16 *)options; options_bytes - 1, options);
s1 = efi_utf16_to_utf8(s1, s2, options_chars);
*s1 = '\0';
*cmd_line_len = options_bytes; *cmd_line_len = options_bytes;
return (char *)cmdline_addr; return (char *)cmdline_addr;
...@@ -284,12 +379,6 @@ void *get_efi_config_table(efi_guid_t guid) ...@@ -284,12 +379,6 @@ void *get_efi_config_table(efi_guid_t guid)
return NULL; 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 * 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 * for the firmware or bootloader to expose the initrd data directly to the stub
...@@ -331,6 +420,7 @@ static const struct { ...@@ -331,6 +420,7 @@ static const struct {
* %EFI_OUT_OF_RESOURCES if memory allocation failed * %EFI_OUT_OF_RESOURCES if memory allocation failed
* %EFI_LOAD_ERROR in all other cases * %EFI_LOAD_ERROR in all other cases
*/ */
static
efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr, efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr,
unsigned long *load_size, unsigned long *load_size,
unsigned long max) unsigned long max)
...@@ -343,9 +433,6 @@ efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr, ...@@ -343,9 +433,6 @@ efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr,
efi_handle_t handle; efi_handle_t handle;
efi_status_t status; efi_status_t status;
if (!load_addr || !load_size)
return EFI_INVALID_PARAMETER;
dp = (efi_device_path_protocol_t *)&initrd_dev_path; dp = (efi_device_path_protocol_t *)&initrd_dev_path;
status = efi_bs_call(locate_device_path, &lf2_proto_guid, &dp, &handle); status = efi_bs_call(locate_device_path, &lf2_proto_guid, &dp, &handle);
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
...@@ -375,3 +462,80 @@ efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr, ...@@ -375,3 +462,80 @@ efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr,
*load_size = initrd_size; *load_size = initrd_size;
return EFI_SUCCESS; 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;
}
...@@ -73,7 +73,7 @@ static void install_memreserve_table(void) ...@@ -73,7 +73,7 @@ static void install_memreserve_table(void)
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, sizeof(*rsv), status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, sizeof(*rsv),
(void **)&rsv); (void **)&rsv);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
pr_efi_err("Failed to allocate memreserve entry!\n"); efi_err("Failed to allocate memreserve entry!\n");
return; return;
} }
...@@ -84,7 +84,7 @@ static void install_memreserve_table(void) ...@@ -84,7 +84,7 @@ static void install_memreserve_table(void)
status = efi_bs_call(install_configuration_table, status = efi_bs_call(install_configuration_table,
&memreserve_table_guid, rsv); &memreserve_table_guid, rsv);
if (status != EFI_SUCCESS) 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) static unsigned long get_dram_base(void)
...@@ -144,7 +144,8 @@ asmlinkage void __noreturn efi_enter_kernel(unsigned long entrypoint, ...@@ -144,7 +144,8 @@ asmlinkage void __noreturn efi_enter_kernel(unsigned long entrypoint,
* for both archictectures, with the arch-specific code provided in the * for both archictectures, with the arch-specific code provided in the
* handle_kernel_image() function. * handle_kernel_image() function.
*/ */
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_loaded_image_t *image;
efi_status_t status; efi_status_t status;
...@@ -186,13 +187,13 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) ...@@ -186,13 +187,13 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
status = efi_system_table->boottime->handle_protocol(handle, status = efi_system_table->boottime->handle_protocol(handle,
&loaded_image_proto, (void *)&image); &loaded_image_proto, (void *)&image);
if (status != EFI_SUCCESS) { 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; goto fail;
} }
dram_base = get_dram_base(); dram_base = get_dram_base();
if (dram_base == EFI_ERROR) { if (dram_base == EFI_ERROR) {
pr_efi_err("Failed to find DRAM base\n"); efi_err("Failed to find DRAM base\n");
status = EFI_LOAD_ERROR; status = EFI_LOAD_ERROR;
goto fail; goto fail;
} }
...@@ -202,22 +203,32 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) ...@@ -202,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 * protocol. We are going to copy the command line into the
* device tree, so this can be allocated anywhere. * device tree, so this can be allocated anywhere.
*/ */
cmdline_ptr = efi_convert_cmdline(image, &cmdline_size, ULONG_MAX); cmdline_ptr = efi_convert_cmdline(image, &cmdline_size);
if (!cmdline_ptr) { 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; status = EFI_OUT_OF_RESOURCES;
goto fail; goto fail;
} }
if (IS_ENABLED(CONFIG_CMDLINE_EXTEND) || if (IS_ENABLED(CONFIG_CMDLINE_EXTEND) ||
IS_ENABLED(CONFIG_CMDLINE_FORCE) || IS_ENABLED(CONFIG_CMDLINE_FORCE) ||
cmdline_size == 0) cmdline_size == 0) {
efi_parse_options(CONFIG_CMDLINE); 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) if (!IS_ENABLED(CONFIG_CMDLINE_FORCE) && cmdline_size > 0) {
efi_parse_options(cmdline_ptr); 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(); si = setup_graphics();
...@@ -226,8 +237,8 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) ...@@ -226,8 +237,8 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
&reserve_size, &reserve_size,
dram_base, image); dram_base, image);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
pr_efi_err("Failed to relocate kernel\n"); efi_err("Failed to relocate kernel\n");
goto fail_free_cmdline; goto fail_free_screeninfo;
} }
efi_retrieve_tpm2_eventlog(); efi_retrieve_tpm2_eventlog();
...@@ -245,42 +256,34 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) ...@@ -245,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) || if (!IS_ENABLED(CONFIG_EFI_ARMSTUB_DTB_LOADER) ||
secure_boot != efi_secureboot_mode_disabled) { secure_boot != efi_secureboot_mode_disabled) {
if (strstr(cmdline_ptr, "dtb=")) if (strstr(cmdline_ptr, "dtb="))
pr_efi("Ignoring DTB from command line.\n"); efi_err("Ignoring DTB from command line.\n");
} else { } else {
status = efi_load_dtb(image, &fdt_addr, &fdt_size); status = efi_load_dtb(image, &fdt_addr, &fdt_size);
if (status != EFI_SUCCESS) { 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; goto fail_free_image;
} }
} }
if (fdt_addr) { if (fdt_addr) {
pr_efi("Using DTB from command line\n"); efi_info("Using DTB from command line\n");
} else { } else {
/* Look for a device tree configuration table entry. */ /* Look for a device tree configuration table entry. */
fdt_addr = (uintptr_t)get_fdt(&fdt_size); fdt_addr = (uintptr_t)get_fdt(&fdt_size);
if (fdt_addr) if (fdt_addr)
pr_efi("Using DTB from configuration table\n"); efi_info("Using DTB from configuration table\n");
} }
if (!fdt_addr) if (!fdt_addr)
pr_efi("Generating empty DTB\n"); efi_info("Generating empty DTB\n");
if (!efi_noinitrd) { if (!efi_noinitrd) {
max_addr = efi_get_max_initrd_addr(dram_base, image_addr); max_addr = efi_get_max_initrd_addr(dram_base, image_addr);
status = efi_load_initrd_dev_path(&initrd_addr, &initrd_size, status = efi_load_initrd(image, &initrd_addr, &initrd_size,
max_addr); ULONG_MAX, 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");
}
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
pr_efi_err("Failed to load initrd!\n"); efi_err("Failed to load initrd!\n");
} }
efi_random_get_seed(); efi_random_get_seed();
...@@ -330,7 +333,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) ...@@ -330,7 +333,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
/* not reached */ /* not reached */
fail_free_initrd: 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(initrd_size, initrd_addr);
efi_free(fdt_size, fdt_addr); efi_free(fdt_size, fdt_addr);
...@@ -338,9 +341,10 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) ...@@ -338,9 +341,10 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
fail_free_image: fail_free_image:
efi_free(image_size, image_addr); efi_free(image_size, image_addr);
efi_free(reserve_size, reserve_addr); efi_free(reserve_size, reserve_addr);
fail_free_cmdline: fail_free_screeninfo:
free_screen_info(si); free_screen_info(si);
efi_free(cmdline_size, (unsigned long)cmdline_ptr); fail_free_cmdline:
efi_bs_call(free_pool, cmdline_ptr);
fail: fail:
return status; return status;
} }
......
...@@ -3,6 +3,13 @@ ...@@ -3,6 +3,13 @@
#ifndef _DRIVERS_FIRMWARE_EFI_EFISTUB_H #ifndef _DRIVERS_FIRMWARE_EFI_EFISTUB_H
#define _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 */ /* error code which can't be mistaken for valid address */
#define EFI_ERROR (~0UL) #define EFI_ERROR (~0UL)
...@@ -28,32 +35,30 @@ ...@@ -28,32 +35,30 @@
extern bool efi_nochunk; extern bool efi_nochunk;
extern bool efi_nokaslr; extern bool efi_nokaslr;
extern bool efi_noinitrd; extern bool efi_noinitrd;
extern bool efi_quiet; extern int efi_loglevel;
extern bool efi_novamap; extern bool efi_novamap;
extern const efi_system_table_t *efi_system_table; extern const efi_system_table_t *efi_system_table;
#ifndef efi_bs_call 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_bs_call(func, ...) efi_system_table->boottime->func(__VA_ARGS__)
#endif
#ifndef efi_rt_call
#define efi_rt_call(func, ...) efi_system_table->runtime->func(__VA_ARGS__) #define efi_rt_call(func, ...) efi_system_table->runtime->func(__VA_ARGS__)
#endif
#ifndef efi_is_native
#define efi_is_native() (true)
#endif
#ifndef efi_table_attr
#define efi_table_attr(inst, attr) (inst->attr) #define efi_table_attr(inst, attr) (inst->attr)
#endif
#ifndef efi_call_proto
#define efi_call_proto(inst, func, ...) inst->func(inst, ##__VA_ARGS__) #define efi_call_proto(inst, func, ...) inst->func(inst, ##__VA_ARGS__)
#endif
#define pr_efi(msg) do { \ #endif
if (!efi_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: */ /* Helper macros for the usual case of using simple C variables: */
#ifndef fdt_setprop_inplace_var #ifndef fdt_setprop_inplace_var
...@@ -87,6 +92,13 @@ extern const efi_system_table_t *efi_system_table; ...@@ -87,6 +92,13 @@ extern const efi_system_table_t *efi_system_table;
((handle = efi_get_handle_at((array), i)) || true); \ ((handle = efi_get_handle_at((array), i)) || true); \
i++) 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. * Allocation types for calls to boottime->allocate_pages.
*/ */
...@@ -102,6 +114,16 @@ extern const efi_system_table_t *efi_system_table; ...@@ -102,6 +114,16 @@ extern const efi_system_table_t *efi_system_table;
#define EFI_LOCATE_BY_REGISTER_NOTIFY 1 #define EFI_LOCATE_BY_REGISTER_NOTIFY 1
#define EFI_LOCATE_BY_PROTOCOL 2 #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 * An efi_boot_memmap is used by efi_get_memory_map() to return the
* EFI memory map in a dynamically allocated buffer. * EFI memory map in a dynamically allocated buffer.
...@@ -126,6 +148,39 @@ struct efi_boot_memmap { ...@@ -126,6 +148,39 @@ struct efi_boot_memmap {
typedef struct efi_generic_dev_path efi_device_path_protocol_t; 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 * EFI Boot Services table
*/ */
...@@ -144,11 +199,16 @@ union efi_boot_services { ...@@ -144,11 +199,16 @@ union efi_boot_services {
efi_status_t (__efiapi *allocate_pool)(int, unsigned long, efi_status_t (__efiapi *allocate_pool)(int, unsigned long,
void **); void **);
efi_status_t (__efiapi *free_pool)(void *); efi_status_t (__efiapi *free_pool)(void *);
void *create_event; efi_status_t (__efiapi *create_event)(u32, unsigned long,
void *set_timer; efi_event_notify_t, void *,
void *wait_for_event; 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 *signal_event;
void *close_event; efi_status_t (__efiapi *close_event)(efi_event_t);
void *check_event; void *check_event;
void *install_protocol_interface; void *install_protocol_interface;
void *reinstall_protocol_interface; void *reinstall_protocol_interface;
...@@ -175,7 +235,7 @@ union efi_boot_services { ...@@ -175,7 +235,7 @@ union efi_boot_services {
efi_status_t (__efiapi *exit_boot_services)(efi_handle_t, efi_status_t (__efiapi *exit_boot_services)(efi_handle_t,
unsigned long); unsigned long);
void *get_next_monotonic_count; void *get_next_monotonic_count;
void *stall; efi_status_t (__efiapi *stall)(unsigned long);
void *set_watchdog_timer; void *set_watchdog_timer;
void *connect_controller; void *connect_controller;
efi_status_t (__efiapi *disconnect_controller)(efi_handle_t, efi_status_t (__efiapi *disconnect_controller)(efi_handle_t,
...@@ -260,6 +320,27 @@ union efi_uga_draw_protocol { ...@@ -260,6 +320,27 @@ union efi_uga_draw_protocol {
} mixed_mode; } 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 { union efi_simple_text_output_protocol {
struct { struct {
void *reset; void *reset;
...@@ -612,8 +693,6 @@ efi_status_t efi_exit_boot_services(void *handle, ...@@ -612,8 +693,6 @@ efi_status_t efi_exit_boot_services(void *handle,
void *priv, void *priv,
efi_exit_boot_map_processing priv_func); efi_exit_boot_map_processing priv_func);
void efi_char16_printk(efi_char16_t *);
efi_status_t allocate_new_fdt_and_exit_boot(void *handle, efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
unsigned long *new_fdt_addr, unsigned long *new_fdt_addr,
unsigned long max_addr, unsigned long max_addr,
...@@ -637,12 +716,15 @@ efi_status_t check_platform_features(void); ...@@ -637,12 +716,15 @@ efi_status_t check_platform_features(void);
void *get_efi_config_table(efi_guid_t guid); void *get_efi_config_table(efi_guid_t guid);
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); void efi_free(unsigned long size, unsigned long addr);
char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len, char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len);
unsigned long max_addr);
efi_status_t efi_get_memory_map(struct efi_boot_memmap *map); efi_status_t efi_get_memory_map(struct efi_boot_memmap *map);
...@@ -683,21 +765,10 @@ static inline efi_status_t efi_load_dtb(efi_loaded_image_t *image, ...@@ -683,21 +765,10 @@ static inline efi_status_t efi_load_dtb(efi_loaded_image_t *image,
ULONG_MAX, ULONG_MAX, load_addr, load_size); ULONG_MAX, ULONG_MAX, load_addr, load_size);
} }
static inline efi_status_t efi_load_initrd(efi_loaded_image_t *image, efi_status_t efi_load_initrd(efi_loaded_image_t *image,
unsigned long *load_addr, unsigned long *load_addr,
unsigned long *load_size, unsigned long *load_size,
unsigned long soft_limit, unsigned long soft_limit,
unsigned long hard_limit) unsigned long hard_limit);
{
if (!IS_ENABLED(CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER))
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_dev_path(unsigned long *load_addr,
unsigned long *load_size,
unsigned long max);
#endif #endif
...@@ -39,7 +39,7 @@ static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size, ...@@ -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: */ /* Do some checks on provided FDT, if it exists: */
if (orig_fdt) { if (orig_fdt) {
if (fdt_check_header(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; return EFI_LOAD_ERROR;
} }
/* /*
...@@ -47,7 +47,7 @@ static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size, ...@@ -47,7 +47,7 @@ static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size,
* configuration table: * configuration table:
*/ */
if (orig_fdt_size && fdt_totalsize(orig_fdt) > orig_fdt_size) { 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; return EFI_LOAD_ERROR;
} }
} }
...@@ -270,16 +270,16 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle, ...@@ -270,16 +270,16 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
*/ */
status = efi_get_memory_map(&map); status = efi_get_memory_map(&map);
if (status != EFI_SUCCESS) { 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; 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; map.map = &memory_map;
status = efi_allocate_pages(MAX_FDT_SIZE, new_fdt_addr, max_addr); status = efi_allocate_pages(MAX_FDT_SIZE, new_fdt_addr, max_addr);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
pr_efi_err("Unable to allocate memory for new device tree.\n"); efi_err("Unable to allocate memory for new device tree.\n");
goto fail; goto fail;
} }
...@@ -296,7 +296,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle, ...@@ -296,7 +296,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
initrd_addr, initrd_size); initrd_addr, initrd_size);
if (status != EFI_SUCCESS) { 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; goto fail_free_new_fdt;
} }
...@@ -342,7 +342,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle, ...@@ -342,7 +342,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
return EFI_SUCCESS; return EFI_SUCCESS;
} }
pr_efi_err("Exit boot services failed.\n"); efi_err("Exit boot services failed.\n");
fail_free_new_fdt: fail_free_new_fdt:
efi_free(MAX_FDT_SIZE, *new_fdt_addr); efi_free(MAX_FDT_SIZE, *new_fdt_addr);
...@@ -363,7 +363,7 @@ void *get_fdt(unsigned long *fdt_size) ...@@ -363,7 +363,7 @@ void *get_fdt(unsigned long *fdt_size)
return NULL; return NULL;
if (fdt_check_header(fdt) != 0) { 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; return NULL;
} }
*fdt_size = fdt_totalsize(fdt); *fdt_size = fdt_totalsize(fdt);
......
...@@ -46,16 +46,14 @@ static efi_status_t efi_open_file(efi_file_protocol_t *volume, ...@@ -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); status = volume->open(volume, &fh, fi->filename, EFI_FILE_MODE_READ, 0);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
pr_efi_err("Failed to open file: "); efi_err("Failed to open file: %ls\n", fi->filename);
efi_char16_printk(fi->filename);
efi_printk("\n");
return status; return status;
} }
info_sz = sizeof(struct finfo); info_sz = sizeof(struct finfo);
status = fh->get_info(fh, &info_guid, &info_sz, fi); status = fh->get_info(fh, &info_guid, &info_sz, fi);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
pr_efi_err("Failed to get file info\n"); efi_err("Failed to get file info\n");
fh->close(fh); fh->close(fh);
return status; return status;
} }
...@@ -75,13 +73,13 @@ static efi_status_t efi_open_volume(efi_loaded_image_t *image, ...@@ -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, status = efi_bs_call(handle_protocol, image->device_handle, &fs_proto,
(void **)&io); (void **)&io);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
pr_efi_err("Failed to handle fs_proto\n"); efi_err("Failed to handle fs_proto\n");
return status; return status;
} }
status = io->open_volume(io, fh); status = io->open_volume(io, fh);
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
pr_efi_err("Failed to open volume\n"); efi_err("Failed to open volume\n");
return status; return status;
} }
...@@ -191,7 +189,7 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image, ...@@ -191,7 +189,7 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
&alloc_addr, &alloc_addr,
hard_limit); hard_limit);
if (status != EFI_SUCCESS) { 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; goto err_close_file;
} }
...@@ -215,7 +213,7 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image, ...@@ -215,7 +213,7 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
status = file->read(file, &chunksize, addr); status = file->read(file, &chunksize, addr);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
pr_efi_err("Failed to read file\n"); efi_err("Failed to read file\n");
goto err_close_file; goto err_close_file;
} }
addr += chunksize; addr += chunksize;
......
...@@ -19,7 +19,8 @@ enum efi_cmdline_option { ...@@ -19,7 +19,8 @@ enum efi_cmdline_option {
EFI_CMDLINE_NONE, EFI_CMDLINE_NONE,
EFI_CMDLINE_MODE_NUM, EFI_CMDLINE_MODE_NUM,
EFI_CMDLINE_RES, EFI_CMDLINE_RES,
EFI_CMDLINE_AUTO EFI_CMDLINE_AUTO,
EFI_CMDLINE_LIST
}; };
static struct { static struct {
...@@ -100,6 +101,19 @@ static bool parse_auto(char *option, char **next) ...@@ -100,6 +101,19 @@ static bool parse_auto(char *option, char **next)
return true; 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) void efi_parse_option_graphics(char *option)
{ {
while (*option) { while (*option) {
...@@ -109,6 +123,8 @@ void efi_parse_option_graphics(char *option) ...@@ -109,6 +123,8 @@ void efi_parse_option_graphics(char *option)
continue; continue;
if (parse_auto(option, &option)) if (parse_auto(option, &option))
continue; continue;
if (parse_list(option, &option))
continue;
while (*option && *option++ != ',') while (*option && *option++ != ',')
; ;
...@@ -134,14 +150,14 @@ static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop) ...@@ -134,14 +150,14 @@ static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop)
max_mode = efi_table_attr(mode, max_mode); max_mode = efi_table_attr(mode, max_mode);
if (cmdline.mode >= max_mode) { if (cmdline.mode >= max_mode) {
efi_printk("Requested mode is invalid\n"); efi_err("Requested mode is invalid\n");
return cur_mode; return cur_mode;
} }
status = efi_call_proto(gop, query_mode, cmdline.mode, status = efi_call_proto(gop, query_mode, cmdline.mode,
&info_size, &info); &info_size, &info);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
efi_printk("Couldn't get mode information\n"); efi_err("Couldn't get mode information\n");
return cur_mode; return cur_mode;
} }
...@@ -150,7 +166,7 @@ static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop) ...@@ -150,7 +166,7 @@ static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop)
efi_bs_call(free_pool, info); efi_bs_call(free_pool, info);
if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) { if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) {
efi_printk("Invalid PixelFormat\n"); efi_err("Invalid PixelFormat\n");
return cur_mode; return cur_mode;
} }
...@@ -222,7 +238,7 @@ static u32 choose_mode_res(efi_graphics_output_protocol_t *gop) ...@@ -222,7 +238,7 @@ static u32 choose_mode_res(efi_graphics_output_protocol_t *gop)
return m; return m;
} }
efi_printk("Couldn't find requested mode\n"); efi_err("Couldn't find requested mode\n");
return cur_mode; return cur_mode;
} }
...@@ -290,6 +306,82 @@ static u32 choose_mode_auto(efi_graphics_output_protocol_t *gop) ...@@ -290,6 +306,82 @@ static u32 choose_mode_auto(efi_graphics_output_protocol_t *gop)
return best_mode; 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) static void set_mode(efi_graphics_output_protocol_t *gop)
{ {
efi_graphics_output_protocol_mode_t *mode; efi_graphics_output_protocol_mode_t *mode;
...@@ -305,6 +397,9 @@ static void set_mode(efi_graphics_output_protocol_t *gop) ...@@ -305,6 +397,9 @@ static void set_mode(efi_graphics_output_protocol_t *gop)
case EFI_CMDLINE_AUTO: case EFI_CMDLINE_AUTO:
new_mode = choose_mode_auto(gop); new_mode = choose_mode_auto(gop);
break; break;
case EFI_CMDLINE_LIST:
new_mode = choose_mode_list(gop);
break;
default: default:
return; return;
} }
...@@ -316,7 +411,7 @@ static void set_mode(efi_graphics_output_protocol_t *gop) ...@@ -316,7 +411,7 @@ static void set_mode(efi_graphics_output_protocol_t *gop)
return; return;
if (efi_call_proto(gop, set_mode, new_mode) != EFI_SUCCESS) if (efi_call_proto(gop, set_mode, new_mode) != EFI_SUCCESS)
efi_printk("Failed to set requested mode\n"); efi_err("Failed to set requested mode\n");
} }
static void find_bits(u32 mask, u8 *pos, u8 *size) static void find_bits(u32 mask, u8 *pos, u8 *size)
...@@ -422,7 +517,6 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, ...@@ -422,7 +517,6 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
efi_graphics_output_protocol_t *gop; efi_graphics_output_protocol_t *gop;
efi_graphics_output_protocol_mode_t *mode; efi_graphics_output_protocol_mode_t *mode;
efi_graphics_output_mode_info_t *info; efi_graphics_output_mode_info_t *info;
efi_physical_addr_t fb_base;
gop = find_gop(proto, size, handles); gop = find_gop(proto, size, handles);
...@@ -442,9 +536,8 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, ...@@ -442,9 +536,8 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
si->lfb_width = info->horizontal_resolution; si->lfb_width = info->horizontal_resolution;
si->lfb_height = info->vertical_resolution; si->lfb_height = info->vertical_resolution;
fb_base = efi_table_attr(mode, frame_buffer_base); efi_set_u64_split(efi_table_attr(mode, frame_buffer_base),
si->lfb_base = lower_32_bits(fb_base); &si->lfb_base, &si->ext_lfb_base);
si->ext_lfb_base = upper_32_bits(fb_base);
if (si->ext_lfb_base) if (si->ext_lfb_base)
si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE; si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
......
...@@ -28,21 +28,21 @@ void efi_pci_disable_bridge_busmaster(void) ...@@ -28,21 +28,21 @@ void efi_pci_disable_bridge_busmaster(void)
if (status != EFI_BUFFER_TOO_SMALL) { if (status != EFI_BUFFER_TOO_SMALL) {
if (status != EFI_SUCCESS && status != EFI_NOT_FOUND) 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; return;
} }
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, pci_handle_size, status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, pci_handle_size,
(void **)&pci_handle); (void **)&pci_handle);
if (status != EFI_SUCCESS) { 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; return;
} }
status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, &pci_proto, status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, &pci_proto,
NULL, &pci_handle_size, pci_handle); NULL, &pci_handle_size, pci_handle);
if (status != EFI_SUCCESS) { 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; goto free_handle;
} }
...@@ -69,7 +69,7 @@ void efi_pci_disable_bridge_busmaster(void) ...@@ -69,7 +69,7 @@ void efi_pci_disable_bridge_busmaster(void)
* access to the framebuffer. Drivers for true PCIe graphics * access to the framebuffer. Drivers for true PCIe graphics
* controllers that are behind a PCIe root port do not use * controllers that are behind a PCIe root port do not use
* DMA to implement the GOP framebuffer anyway [although they * 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 * disabling DMA in the PCI bridge should not interfere with
* normal operation of the device. * normal operation of the device.
*/ */
...@@ -106,7 +106,7 @@ void efi_pci_disable_bridge_busmaster(void) ...@@ -106,7 +106,7 @@ void efi_pci_disable_bridge_busmaster(void)
status = efi_call_proto(pci, pci.write, EfiPciIoWidthUint16, status = efi_call_proto(pci, pci.write, EfiPciIoWidthUint16,
PCI_COMMAND, 1, &command); PCI_COMMAND, 1, &command);
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
pr_efi_err("Failed to disable PCI busmastering\n"); efi_err("Failed to disable PCI busmastering\n");
} }
free_handle: free_handle:
......
...@@ -140,7 +140,7 @@ efi_status_t efi_relocate_kernel(unsigned long *image_addr, ...@@ -140,7 +140,7 @@ efi_status_t efi_relocate_kernel(unsigned long *image_addr,
* The EFI firmware loader could have placed the kernel image * The EFI firmware loader could have placed the kernel image
* anywhere in memory, but the kernel has restrictions on the * anywhere in memory, but the kernel has restrictions on the
* max physical address it can run at. Some architectures * max physical address it can run at. Some architectures
* also have a prefered address, so first try to relocate * also have a preferred address, so first try to relocate
* to the preferred address. If that fails, allocate as low * to the preferred address. If that fails, allocate as low
* as possible while respecting the required alignment. * as possible while respecting the required alignment.
*/ */
...@@ -157,7 +157,7 @@ efi_status_t efi_relocate_kernel(unsigned long *image_addr, ...@@ -157,7 +157,7 @@ efi_status_t efi_relocate_kernel(unsigned long *image_addr,
min_addr); min_addr);
} }
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
pr_efi_err("Failed to allocate usable memory for kernel.\n"); efi_err("Failed to allocate usable memory for kernel.\n");
return status; return status;
} }
......
...@@ -67,10 +67,10 @@ enum efi_secureboot_mode efi_get_secureboot(void) ...@@ -67,10 +67,10 @@ enum efi_secureboot_mode efi_get_secureboot(void)
return efi_secureboot_mode_disabled; return efi_secureboot_mode_disabled;
secure_boot_enabled: secure_boot_enabled:
pr_efi("UEFI Secure Boot is enabled.\n"); efi_info("UEFI Secure Boot is enabled.\n");
return efi_secureboot_mode_enabled; return efi_secureboot_mode_enabled;
out_efi_err: 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; return efi_secureboot_mode_unknown;
} }
...@@ -119,7 +119,7 @@ void efi_retrieve_tpm2_eventlog(void) ...@@ -119,7 +119,7 @@ void efi_retrieve_tpm2_eventlog(void)
sizeof(*log_tbl) + log_size, (void **)&log_tbl); sizeof(*log_tbl) + log_size, (void **)&log_tbl);
if (status != EFI_SUCCESS) { 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; return;
} }
......
// 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;
}
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
const efi_system_table_t *efi_system_table; const efi_system_table_t *efi_system_table;
extern u32 image_offset; extern u32 image_offset;
static efi_loaded_image_t *image = NULL;
static efi_status_t static efi_status_t
preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom) preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
...@@ -49,7 +50,7 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom) ...@@ -49,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, status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, size,
(void **)&rom); (void **)&rom);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
efi_printk("Failed to allocate memory for 'rom'\n"); efi_err("Failed to allocate memory for 'rom'\n");
return status; return status;
} }
...@@ -65,7 +66,7 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom) ...@@ -65,7 +66,7 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
PCI_VENDOR_ID, 1, &rom->vendor); PCI_VENDOR_ID, 1, &rom->vendor);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
efi_printk("Failed to read rom->vendor\n"); efi_err("Failed to read rom->vendor\n");
goto free_struct; goto free_struct;
} }
...@@ -73,7 +74,7 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom) ...@@ -73,7 +74,7 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
PCI_DEVICE_ID, 1, &rom->devid); PCI_DEVICE_ID, 1, &rom->devid);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
efi_printk("Failed to read rom->devid\n"); efi_err("Failed to read rom->devid\n");
goto free_struct; goto free_struct;
} }
...@@ -118,7 +119,7 @@ static void setup_efi_pci(struct boot_params *params) ...@@ -118,7 +119,7 @@ static void setup_efi_pci(struct boot_params *params)
(void **)&pci_handle); (void **)&pci_handle);
if (status != EFI_SUCCESS) { 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; return;
} }
...@@ -172,7 +173,7 @@ static void retrieve_apple_device_properties(struct boot_params *boot_params) ...@@ -172,7 +173,7 @@ static void retrieve_apple_device_properties(struct boot_params *boot_params)
return; return;
if (efi_table_attr(p, version) != 0x10000) { if (efi_table_attr(p, version) != 0x10000) {
efi_printk("Unsupported properties proto version\n"); efi_err("Unsupported properties proto version\n");
return; return;
} }
...@@ -185,7 +186,7 @@ static void retrieve_apple_device_properties(struct boot_params *boot_params) ...@@ -185,7 +186,7 @@ static void retrieve_apple_device_properties(struct boot_params *boot_params)
size + sizeof(struct setup_data), size + sizeof(struct setup_data),
(void **)&new); (void **)&new);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
efi_printk("Failed to allocate memory for 'properties'\n"); efi_err("Failed to allocate memory for 'properties'\n");
return; return;
} }
...@@ -355,7 +356,6 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, ...@@ -355,7 +356,6 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
{ {
struct boot_params *boot_params; struct boot_params *boot_params;
struct setup_header *hdr; struct setup_header *hdr;
efi_loaded_image_t *image;
void *image_base; void *image_base;
efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID; efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
int options_size = 0; int options_size = 0;
...@@ -372,20 +372,21 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, ...@@ -372,20 +372,21 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
status = efi_bs_call(handle_protocol, handle, &proto, (void **)&image); status = efi_bs_call(handle_protocol, handle, &proto, (void **)&image);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
efi_printk("Failed to get handle for LOADED_IMAGE_PROTOCOL\n"); efi_err("Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
efi_exit(handle, status); efi_exit(handle, status);
} }
image_base = efi_table_attr(image, image_base); image_base = efi_table_attr(image, image_base);
image_offset = (void *)startup_32 - 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) { 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); efi_exit(handle, status);
} }
memset(boot_params, 0x0, 0x4000); memset(boot_params, 0x0, sizeof(struct boot_params));
hdr = &boot_params->hdr; hdr = &boot_params->hdr;
...@@ -403,43 +404,21 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, ...@@ -403,43 +404,21 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
hdr->type_of_loader = 0x21; hdr->type_of_loader = 0x21;
/* Convert unicode cmdline to ascii */ /* Convert unicode cmdline to ascii */
cmdline_ptr = efi_convert_cmdline(image, &options_size, ULONG_MAX); cmdline_ptr = efi_convert_cmdline(image, &options_size);
if (!cmdline_ptr) if (!cmdline_ptr)
goto fail; goto fail;
hdr->cmd_line_ptr = (unsigned long)cmdline_ptr; efi_set_u64_split((unsigned long)cmdline_ptr,
/* Fill in upper bits of command line address, NOP on 32 bit */ &hdr->cmd_line_ptr, &boot_params->ext_cmd_line_ptr);
boot_params->ext_cmd_line_ptr = (u64)(unsigned long)cmdline_ptr >> 32;
hdr->ramdisk_image = 0; hdr->ramdisk_image = 0;
hdr->ramdisk_size = 0; hdr->ramdisk_size = 0;
if (efi_is_native()) {
status = efi_parse_options(cmdline_ptr);
if (status != EFI_SUCCESS)
goto fail2;
if (!efi_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_arg, boot_params); efi_stub_entry(handle, sys_table_arg, boot_params);
/* not reached */ /* not reached */
fail2:
efi_free(options_size, (unsigned long)cmdline_ptr);
fail: fail:
efi_free(0x4000, (unsigned long)boot_params); efi_free(sizeof(struct boot_params), (unsigned long)boot_params);
efi_exit(handle, status); efi_exit(handle, status);
} }
...@@ -632,17 +611,14 @@ static efi_status_t exit_boot_func(struct efi_boot_memmap *map, ...@@ -632,17 +611,14 @@ static efi_status_t exit_boot_func(struct efi_boot_memmap *map,
: EFI32_LOADER_SIGNATURE; : EFI32_LOADER_SIGNATURE;
memcpy(&p->efi->efi_loader_signature, signature, sizeof(__u32)); 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_size = *map->desc_size;
p->efi->efi_memdesc_version = *map->desc_ver; 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; 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; return EFI_SUCCESS;
} }
...@@ -698,7 +674,6 @@ unsigned long efi_main(efi_handle_t handle, ...@@ -698,7 +674,6 @@ unsigned long efi_main(efi_handle_t handle,
unsigned long buffer_start, buffer_end; unsigned long buffer_start, buffer_end;
struct setup_header *hdr = &boot_params->hdr; struct setup_header *hdr = &boot_params->hdr;
efi_status_t status; efi_status_t status;
unsigned long cmdline_paddr;
efi_system_table = sys_table_arg; efi_system_table = sys_table_arg;
...@@ -746,7 +721,7 @@ unsigned long efi_main(efi_handle_t handle, ...@@ -746,7 +721,7 @@ unsigned long efi_main(efi_handle_t handle,
hdr->kernel_alignment, hdr->kernel_alignment,
LOAD_PHYSICAL_ADDR); LOAD_PHYSICAL_ADDR);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
efi_printk("efi_relocate_kernel() failed!\n"); efi_err("efi_relocate_kernel() failed!\n");
goto fail; goto fail;
} }
/* /*
...@@ -757,35 +732,46 @@ unsigned long efi_main(efi_handle_t handle, ...@@ -757,35 +732,46 @@ unsigned long efi_main(efi_handle_t handle,
image_offset = 0; image_offset = 0;
} }
/* #ifdef CONFIG_CMDLINE_BOOL
* efi_pe_entry() may have been called before efi_main(), in which status = efi_parse_options(CONFIG_CMDLINE);
* case this is the second time we parse the cmdline. This is ok, if (status != EFI_SUCCESS) {
* parsing the cmdline multiple times does not have side-effects. efi_err("Failed to parse options\n");
*/ goto fail;
cmdline_paddr = ((u64)hdr->cmd_line_ptr | }
((u64)boot_params->ext_cmd_line_ptr << 32)); #endif
efi_parse_options((char *)cmdline_paddr); 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 * At this point, an initrd may already have been loaded by the
* the bootloader and passed via bootparams, or loaded from a initrd= * bootloader and passed via bootparams. We permit an initrd loaded
* command line option by efi_pe_entry() above. In either case, we * from the LINUX_EFI_INITRD_MEDIA_GUID device path to supersede it.
* 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 (!efi_noinitrd) { if (!efi_noinitrd) {
unsigned long addr, size; unsigned long addr, size;
status = efi_load_initrd_dev_path(&addr, &size, ULONG_MAX); status = efi_load_initrd(image, &addr, &size,
if (status == EFI_SUCCESS) { hdr->initrd_addr_max, ULONG_MAX);
hdr->ramdisk_image = (u32)addr;
hdr->ramdisk_size = (u32)size; if (status != EFI_SUCCESS) {
boot_params->ext_ramdisk_image = (u64)addr >> 32; efi_err("Failed to load initrd!\n");
boot_params->ext_ramdisk_size = (u64)size >> 32;
} else if (status != EFI_NOT_FOUND) {
efi_printk("efi_load_initrd_dev_path() failed!\n");
goto fail; goto fail;
} }
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);
} }
/* /*
...@@ -810,13 +796,13 @@ unsigned long efi_main(efi_handle_t handle, ...@@ -810,13 +796,13 @@ unsigned long efi_main(efi_handle_t handle,
status = exit_boot(boot_params, handle); status = exit_boot(boot_params, handle);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
efi_printk("exit_boot() failed!\n"); efi_err("exit_boot() failed!\n");
goto fail; goto fail;
} }
return bzimage_addr; return bzimage_addr;
fail: fail:
efi_printk("efi_main() failed!\n"); efi_err("efi_main() failed!\n");
efi_exit(handle, status); efi_exit(handle, status);
} }
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#define EFI_WRITE_PROTECTED ( 8 | (1UL << (BITS_PER_LONG-1))) #define EFI_WRITE_PROTECTED ( 8 | (1UL << (BITS_PER_LONG-1)))
#define EFI_OUT_OF_RESOURCES ( 9 | (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_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_ABORTED (21 | (1UL << (BITS_PER_LONG-1)))
#define EFI_SECURITY_VIOLATION (26 | (1UL << (BITS_PER_LONG-1))) #define EFI_SECURITY_VIOLATION (26 | (1UL << (BITS_PER_LONG-1)))
...@@ -426,6 +427,7 @@ typedef struct { ...@@ -426,6 +427,7 @@ typedef struct {
u32 tables; u32 tables;
} efi_system_table_32_t; } 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 efi_simple_text_output_protocol efi_simple_text_output_protocol_t;
typedef union { typedef union {
...@@ -434,7 +436,7 @@ typedef union { ...@@ -434,7 +436,7 @@ typedef union {
unsigned long fw_vendor; /* physical addr of CHAR16 vendor string */ unsigned long fw_vendor; /* physical addr of CHAR16 vendor string */
u32 fw_revision; u32 fw_revision;
unsigned long con_in_handle; unsigned long con_in_handle;
unsigned long con_in; efi_simple_text_input_protocol_t *con_in;
unsigned long con_out_handle; unsigned long con_out_handle;
efi_simple_text_output_protocol_t *con_out; efi_simple_text_output_protocol_t *con_out;
unsigned long stderr_handle; unsigned long stderr_handle;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment