mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 20:22:09 +00:00
efi: libstub: Enable efi_printk() in zboot decompressor
Split the efi_printk() routine into its own source file, and provide local implementations of strlen() and strnlen() so that the standalone zboot app can efi_err and efi_info etc. Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
This commit is contained in:
parent
52dce39cd2
commit
2e6fa86f2d
@ -22,8 +22,6 @@ PROVIDE(__efistub_primary_entry_offset = primary_entry - _text);
|
||||
* position independent manner
|
||||
*/
|
||||
PROVIDE(__efistub_memchr = __pi_memchr);
|
||||
PROVIDE(__efistub_strlen = __pi_strlen);
|
||||
PROVIDE(__efistub_strnlen = __pi_strnlen);
|
||||
PROVIDE(__efistub_strcmp = __pi_strcmp);
|
||||
PROVIDE(__efistub_strrchr = __pi_strrchr);
|
||||
PROVIDE(__efistub_dcache_clean_poc = __pi_dcache_clean_poc);
|
||||
|
@ -10,10 +10,8 @@
|
||||
__efistub_memchr = memchr;
|
||||
__efistub_strcat = strcat;
|
||||
__efistub_strcmp = strcmp;
|
||||
__efistub_strlen = strlen;
|
||||
__efistub_strncat = strncat;
|
||||
__efistub_strnstr = strnstr;
|
||||
__efistub_strnlen = strnlen;
|
||||
__efistub_strrchr = strrchr;
|
||||
__efistub_kernel_entry = kernel_entry;
|
||||
__efistub_kernel_asize = kernel_asize;
|
||||
|
@ -24,8 +24,6 @@
|
||||
* position independent manner
|
||||
*/
|
||||
__efistub_memchr = memchr;
|
||||
__efistub_strlen = strlen;
|
||||
__efistub_strnlen = strnlen;
|
||||
__efistub_strcmp = strcmp;
|
||||
__efistub_strrchr = strrchr;
|
||||
|
||||
|
@ -24,7 +24,8 @@ cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ \
|
||||
# disable the stackleak plugin
|
||||
cflags-$(CONFIG_ARM64) += -fpie $(DISABLE_STACKLEAK_PLUGIN) \
|
||||
$(call cc-option,-mbranch-protection=none)
|
||||
cflags-$(CONFIG_ARM) += -fno-builtin -fpic \
|
||||
cflags-$(CONFIG_ARM) += -DEFI_HAVE_STRLEN -DEFI_HAVE_STRNLEN \
|
||||
-fno-builtin -fpic \
|
||||
$(call cc-option,-mno-single-pic-base)
|
||||
cflags-$(CONFIG_RISCV) += -fpic
|
||||
cflags-$(CONFIG_LOONGARCH) += -fpie
|
||||
@ -68,7 +69,7 @@ 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 \
|
||||
alignedmem.o relocate.o vsprintf.o
|
||||
alignedmem.o relocate.o printk.o vsprintf.o
|
||||
|
||||
# include the stub's libfdt dependencies from lib/ when needed
|
||||
libfdt-deps := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c \
|
||||
|
@ -9,10 +9,8 @@
|
||||
|
||||
#include <linux/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>
|
||||
|
||||
@ -20,7 +18,6 @@
|
||||
|
||||
bool efi_nochunk;
|
||||
bool efi_nokaslr = !IS_ENABLED(CONFIG_RANDOMIZE_BASE);
|
||||
int efi_loglevel = CONSOLE_LOGLEVEL_DEFAULT;
|
||||
bool efi_novamap;
|
||||
|
||||
static bool efi_noinitrd;
|
||||
@ -32,146 +29,6 @@ bool __pure __efi_soft_reserve_enabled(void)
|
||||
return !efi_nosoftreserve;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_char16_puts() - Write a UCS-2 encoded string to the console
|
||||
* @str: UCS-2 encoded string
|
||||
*/
|
||||
void efi_char16_puts(efi_char16_t *str)
|
||||
{
|
||||
efi_call_proto(efi_table_attr(efi_system_table, con_out),
|
||||
output_string, str);
|
||||
}
|
||||
|
||||
static
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_puts() - Write a UTF-8 encoded string to the console
|
||||
* @str: UTF-8 encoded string
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_printk() - Print a kernel message
|
||||
* @fmt: format string
|
||||
*
|
||||
* The first letter of the format string is used to determine the logging level
|
||||
* of the message. If the level is less then the current EFI logging level, the
|
||||
* message is suppressed. The message will be truncated to 255 bytes.
|
||||
*
|
||||
* Return: number of printed characters
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_parse_options() - Parse EFI command line options
|
||||
* @cmdline: kernel command line
|
||||
|
154
drivers/firmware/efi/libstub/printk.c
Normal file
154
drivers/firmware/efi/libstub/printk.c
Normal file
@ -0,0 +1,154 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/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"
|
||||
|
||||
int efi_loglevel = CONSOLE_LOGLEVEL_DEFAULT;
|
||||
|
||||
/**
|
||||
* efi_char16_puts() - Write a UCS-2 encoded string to the console
|
||||
* @str: UCS-2 encoded string
|
||||
*/
|
||||
void efi_char16_puts(efi_char16_t *str)
|
||||
{
|
||||
efi_call_proto(efi_table_attr(efi_system_table, con_out),
|
||||
output_string, str);
|
||||
}
|
||||
|
||||
static
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_puts() - Write a UTF-8 encoded string to the console
|
||||
* @str: UTF-8 encoded string
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_printk() - Print a kernel message
|
||||
* @fmt: format string
|
||||
*
|
||||
* The first letter of the format string is used to determine the logging level
|
||||
* of the message. If the level is less then the current EFI logging level, the
|
||||
* message is suppressed. The message will be truncated to 255 bytes.
|
||||
*
|
||||
* Return: number of printed characters
|
||||
*/
|
||||
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;
|
||||
}
|
@ -11,7 +11,37 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#ifndef __HAVE_ARCH_STRSTR
|
||||
#ifndef EFI_HAVE_STRLEN
|
||||
/**
|
||||
* strlen - Find the length of a string
|
||||
* @s: The string to be sized
|
||||
*/
|
||||
size_t strlen(const char *s)
|
||||
{
|
||||
const char *sc;
|
||||
|
||||
for (sc = s; *sc != '\0'; ++sc)
|
||||
/* nothing */;
|
||||
return sc - s;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef EFI_HAVE_STRNLEN
|
||||
/**
|
||||
* strnlen - Find the length of a length-limited string
|
||||
* @s: The string to be sized
|
||||
* @count: The maximum number of bytes to search
|
||||
*/
|
||||
size_t strnlen(const char *s, size_t count)
|
||||
{
|
||||
const char *sc;
|
||||
|
||||
for (sc = s; count-- && *sc != '\0'; ++sc)
|
||||
/* nothing */;
|
||||
return sc - s;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* strstr - Find the first substring in a %NUL terminated string
|
||||
* @s1: The string to be searched
|
||||
@ -33,7 +63,6 @@ char *strstr(const char *s1, const char *s2)
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* strncmp - Compare two length-limited strings
|
||||
|
@ -32,19 +32,9 @@ static unsigned long free_mem_ptr, free_mem_end_ptr;
|
||||
extern char efi_zboot_header[];
|
||||
extern char _gzdata_start[], _gzdata_end[];
|
||||
|
||||
static void log(efi_char16_t str[])
|
||||
{
|
||||
efi_call_proto(efi_table_attr(efi_system_table, con_out),
|
||||
output_string, L"EFI decompressor: ");
|
||||
efi_call_proto(efi_table_attr(efi_system_table, con_out),
|
||||
output_string, str);
|
||||
efi_call_proto(efi_table_attr(efi_system_table, con_out),
|
||||
output_string, L"\n");
|
||||
}
|
||||
|
||||
static void error(char *x)
|
||||
{
|
||||
log(L"error() called from decompressor library\n");
|
||||
efi_err("EFI decompressor: %s\n", x);
|
||||
}
|
||||
|
||||
static efi_status_t __efiapi
|
||||
@ -91,7 +81,7 @@ load_file(efi_load_file_protocol_t *this, efi_device_path_protocol_t *rem,
|
||||
ret = __decompress(_gzdata_start, compressed_size, NULL, NULL,
|
||||
buffer, size, NULL, error);
|
||||
if (ret < 0) {
|
||||
log(L"Decompression failed");
|
||||
error("Decompression failed");
|
||||
return EFI_DEVICE_ERROR;
|
||||
}
|
||||
} else {
|
||||
@ -180,7 +170,7 @@ efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab)
|
||||
status = efi_bs_call(handle_protocol, handle,
|
||||
&LOADED_IMAGE_PROTOCOL_GUID, (void **)&parent);
|
||||
if (status != EFI_SUCCESS) {
|
||||
log(L"Failed to locate parent's loaded image protocol");
|
||||
error("Failed to locate parent's loaded image protocol");
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -207,7 +197,7 @@ efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab)
|
||||
sizeof(struct efi_vendor_dev_path),
|
||||
(void **)&dp_alloc);
|
||||
if (status != EFI_SUCCESS) {
|
||||
log(L"Failed to allocate device path pool memory");
|
||||
error("Failed to allocate device path pool memory");
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -236,21 +226,21 @@ efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab)
|
||||
&EFI_LOAD_FILE2_PROTOCOL_GUID, &zboot_load_file2,
|
||||
NULL);
|
||||
if (status != EFI_SUCCESS) {
|
||||
log(L"Failed to install LoadFile2 protocol and device path");
|
||||
error("Failed to install LoadFile2 protocol and device path");
|
||||
goto free_dpalloc;
|
||||
}
|
||||
|
||||
status = efi_bs_call(load_image, false, handle, li_dp, NULL, 0,
|
||||
&child_handle);
|
||||
if (status != EFI_SUCCESS) {
|
||||
log(L"Failed to load image");
|
||||
error("Failed to load image");
|
||||
goto uninstall_lf2;
|
||||
}
|
||||
|
||||
status = efi_bs_call(handle_protocol, child_handle,
|
||||
&LOADED_IMAGE_PROTOCOL_GUID, (void **)&child);
|
||||
if (status != EFI_SUCCESS) {
|
||||
log(L"Failed to locate child's loaded image protocol");
|
||||
error("Failed to locate child's loaded image protocol");
|
||||
goto unload_image;
|
||||
}
|
||||
|
||||
@ -261,9 +251,9 @@ efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab)
|
||||
status = efi_bs_call(start_image, child_handle, &exit_data_size,
|
||||
&exit_data);
|
||||
if (status != EFI_SUCCESS) {
|
||||
log(L"StartImage() returned with error");
|
||||
error("StartImage() returned with error:");
|
||||
if (exit_data_size > 0)
|
||||
log(exit_data);
|
||||
efi_err("%ls\n", exit_data);
|
||||
|
||||
// If StartImage() returns EFI_SECURITY_VIOLATION, the image is
|
||||
// not unloaded so we need to do it by hand.
|
||||
@ -286,7 +276,7 @@ free_dpalloc:
|
||||
|
||||
// Free ExitData in case Exit() returned with a failure code,
|
||||
// but return the original status code.
|
||||
log(L"Exit() returned with failure code");
|
||||
error("Exit() returned with failure code");
|
||||
if (exit_data != NULL)
|
||||
efi_bs_call(free_pool, exit_data);
|
||||
return status;
|
||||
|
Loading…
Reference in New Issue
Block a user