efi: libstub: Enable efi_printk() in zboot decompressor
authorArd Biesheuvel <ardb@kernel.org>
Tue, 11 Oct 2022 13:27:45 +0000 (15:27 +0200)
committerArd Biesheuvel <ardb@kernel.org>
Wed, 9 Nov 2022 11:42:02 +0000 (12:42 +0100)
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>
arch/arm64/kernel/image-vars.h
arch/loongarch/kernel/image-vars.h
arch/riscv/kernel/image-vars.h
drivers/firmware/efi/libstub/Makefile
drivers/firmware/efi/libstub/efi-stub-helper.c
drivers/firmware/efi/libstub/printk.c [new file with mode: 0644]
drivers/firmware/efi/libstub/string.c
drivers/firmware/efi/libstub/zboot.c

index f7c8cdf..ace584e 100644 (file)
@@ -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);
index 5b6c7f0..28bc632 100644 (file)
 __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;
index 9229cfe..4913abd 100644 (file)
@@ -24,8 +24,6 @@
  * position independent manner
  */
 __efistub_memchr               = memchr;
-__efistub_strlen               = strlen;
-__efistub_strnlen              = strnlen;
 __efistub_strcmp               = strcmp;
 __efistub_strrchr              = strrchr;
 
index 01a0be4..5b0ae75 100644 (file)
@@ -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 \
index 0c49352..2184f3f 100644 (file)
@@ -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 (file)
index 0000000..3a67a2c
--- /dev/null
@@ -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;
+}
index 9f5810d..5a1519d 100644 (file)
 #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
index 38173ec..5f41a5b 100644 (file)
@@ -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;