From 2e6fa86f2d484ff9a0047e20a48c1400314a5bb6 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 11 Oct 2022 15:27:45 +0200 Subject: [PATCH] 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 --- arch/arm64/kernel/image-vars.h | 2 - arch/loongarch/kernel/image-vars.h | 2 - arch/riscv/kernel/image-vars.h | 2 - drivers/firmware/efi/libstub/Makefile | 5 +- drivers/firmware/efi/libstub/efi-stub-helper.c | 143 ----------------------- drivers/firmware/efi/libstub/printk.c | 154 +++++++++++++++++++++++++ drivers/firmware/efi/libstub/string.c | 33 +++++- drivers/firmware/efi/libstub/zboot.c | 30 ++--- 8 files changed, 198 insertions(+), 173 deletions(-) create mode 100644 drivers/firmware/efi/libstub/printk.c diff --git a/arch/arm64/kernel/image-vars.h b/arch/arm64/kernel/image-vars.h index f7c8cdf..ace584e 100644 --- a/arch/arm64/kernel/image-vars.h +++ b/arch/arm64/kernel/image-vars.h @@ -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); diff --git a/arch/loongarch/kernel/image-vars.h b/arch/loongarch/kernel/image-vars.h index 5b6c7f0..28bc632 100644 --- a/arch/loongarch/kernel/image-vars.h +++ b/arch/loongarch/kernel/image-vars.h @@ -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; diff --git a/arch/riscv/kernel/image-vars.h b/arch/riscv/kernel/image-vars.h index 9229cfe..4913abd 100644 --- a/arch/riscv/kernel/image-vars.h +++ b/arch/riscv/kernel/image-vars.h @@ -24,8 +24,6 @@ * position independent manner */ __efistub_memchr = memchr; -__efistub_strlen = strlen; -__efistub_strnlen = strnlen; __efistub_strcmp = strcmp; __efistub_strrchr = strrchr; diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 01a0be4..5b0ae75 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -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 \ diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 0c49352..2184f3f 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -9,10 +9,8 @@ #include -#include #include #include -#include /* For CONSOLE_LOGLEVEL_* */ #include #include @@ -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; @@ -33,146 +30,6 @@ bool __pure __efi_soft_reserve_enabled(void) } /** - * 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 * diff --git a/drivers/firmware/efi/libstub/printk.c b/drivers/firmware/efi/libstub/printk.c new file mode 100644 index 0000000..3a67a2c --- /dev/null +++ b/drivers/firmware/efi/libstub/printk.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +#include +#include +#include +#include /* For CONSOLE_LOGLEVEL_* */ +#include +#include + +#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; +} diff --git a/drivers/firmware/efi/libstub/string.c b/drivers/firmware/efi/libstub/string.c index 9f5810d..5a1519d 100644 --- a/drivers/firmware/efi/libstub/string.c +++ b/drivers/firmware/efi/libstub/string.c @@ -11,7 +11,37 @@ #include #include -#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 diff --git a/drivers/firmware/efi/libstub/zboot.c b/drivers/firmware/efi/libstub/zboot.c index 38173ec..5f41a5b 100644 --- a/drivers/firmware/efi/libstub/zboot.c +++ b/drivers/firmware/efi/libstub/zboot.c @@ -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; -- 2.7.4