summaryrefslogtreecommitdiffstats
path: root/drivers/firmware/efi
diff options
context:
space:
mode:
authorArd Biesheuvel <ardb@kernel.org>2022-10-11 15:27:45 +0200
committerArd Biesheuvel <ardb@kernel.org>2022-11-09 12:42:02 +0100
commit2e6fa86f2d484ff9a0047e20a48c1400314a5bb6 (patch)
tree4ff6658afebecdf141b3170f148069ea0bc41474 /drivers/firmware/efi
parent52dce39cd2786673969a12d1c94e0922d9208f83 (diff)
downloadlinux-2e6fa86f2d484ff9a0047e20a48c1400314a5bb6.tar.bz2
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>
Diffstat (limited to 'drivers/firmware/efi')
-rw-r--r--drivers/firmware/efi/libstub/Makefile5
-rw-r--r--drivers/firmware/efi/libstub/efi-stub-helper.c143
-rw-r--r--drivers/firmware/efi/libstub/printk.c154
-rw-r--r--drivers/firmware/efi/libstub/string.c33
-rw-r--r--drivers/firmware/efi/libstub/zboot.c30
5 files changed, 198 insertions, 167 deletions
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index 01a0be468a66..5b0ae755180e 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 0c493521b25b..2184f3f153ef 100644
--- a/drivers/firmware/efi/libstub/efi-stub-helper.c
+++ b/drivers/firmware/efi/libstub/efi-stub-helper.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;
@@ -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 000000000000..3a67a2cea7bd
--- /dev/null
+++ b/drivers/firmware/efi/libstub/printk.c
@@ -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;
+}
diff --git a/drivers/firmware/efi/libstub/string.c b/drivers/firmware/efi/libstub/string.c
index 9f5810d86646..5a1519d435bf 100644
--- a/drivers/firmware/efi/libstub/string.c
+++ b/drivers/firmware/efi/libstub/string.c
@@ -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
diff --git a/drivers/firmware/efi/libstub/zboot.c b/drivers/firmware/efi/libstub/zboot.c
index 38173ec29cd5..5f41a5b17d6e 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;