// 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; }