Path: blob/master/drivers/firmware/efi/libstub/printk.c
26483 views
// SPDX-License-Identifier: GPL-2.012#include <linux/stdarg.h>34#include <linux/ctype.h>5#include <linux/efi.h>6#include <linux/kernel.h>7#include <linux/kern_levels.h>8#include <asm/efi.h>9#include <asm/setup.h>1011#include "efistub.h"1213int efi_loglevel = LOGLEVEL_NOTICE;1415/**16* efi_char16_puts() - Write a UCS-2 encoded string to the console17* @str: UCS-2 encoded string18*/19void efi_char16_puts(efi_char16_t *str)20{21efi_call_proto(efi_table_attr(efi_system_table, con_out),22output_string, str);23}2425static26u32 utf8_to_utf32(const u8 **s8)27{28u32 c32;29u8 c0, cx;30size_t clen, i;3132c0 = cx = *(*s8)++;33/*34* The position of the most-significant 0 bit gives us the length of35* a multi-octet encoding.36*/37for (clen = 0; cx & 0x80; ++clen)38cx <<= 1;39/*40* If the 0 bit is in position 8, this is a valid single-octet41* encoding. If the 0 bit is in position 7 or positions 1-3, the42* encoding is invalid.43* In either case, we just return the first octet.44*/45if (clen < 2 || clen > 4)46return c0;47/* Get the bits from the first octet. */48c32 = cx >> clen--;49for (i = 0; i < clen; ++i) {50/* Trailing octets must have 10 in most significant bits. */51cx = (*s8)[i] ^ 0x80;52if (cx & 0xc0)53return c0;54c32 = (c32 << 6) | cx;55}56/*57* Check for validity:58* - The character must be in the Unicode range.59* - It must not be a surrogate.60* - It must be encoded using the correct number of octets.61*/62if (c32 > 0x10ffff ||63(c32 & 0xf800) == 0xd800 ||64clen != (c32 >= 0x80) + (c32 >= 0x800) + (c32 >= 0x10000))65return c0;66*s8 += clen;67return c32;68}6970/**71* efi_puts() - Write a UTF-8 encoded string to the console72* @str: UTF-8 encoded string73*/74void efi_puts(const char *str)75{76efi_char16_t buf[128];77size_t pos = 0, lim = ARRAY_SIZE(buf);78const u8 *s8 = (const u8 *)str;79u32 c32;8081while (*s8) {82if (*s8 == '\n')83buf[pos++] = L'\r';84c32 = utf8_to_utf32(&s8);85if (c32 < 0x10000) {86/* Characters in plane 0 use a single word. */87buf[pos++] = c32;88} else {89/*90* Characters in other planes encode into a surrogate91* pair.92*/93buf[pos++] = (0xd800 - (0x10000 >> 10)) + (c32 >> 10);94buf[pos++] = 0xdc00 + (c32 & 0x3ff);95}96if (*s8 == '\0' || pos >= lim - 2) {97buf[pos] = L'\0';98efi_char16_puts(buf);99pos = 0;100}101}102}103104/**105* efi_printk() - Print a kernel message106* @fmt: format string107*108* The first letter of the format string is used to determine the logging level109* of the message. If the level is less then the current EFI logging level, the110* message is suppressed. The message will be truncated to 255 bytes.111*112* Return: number of printed characters113*/114int efi_printk(const char *fmt, ...)115{116char printf_buf[256];117va_list args;118int printed;119int loglevel = printk_get_level(fmt);120121switch (loglevel) {122case '0' ... '9':123loglevel -= '0';124break;125default:126/*127* Use loglevel -1 for cases where we just want to print to128* the screen.129*/130loglevel = -1;131break;132}133134if (loglevel >= efi_loglevel)135return 0;136137if (loglevel >= 0)138efi_puts("EFI stub: ");139140fmt = printk_skip_level(fmt);141142va_start(args, fmt);143printed = vsnprintf(printf_buf, sizeof(printf_buf), fmt, args);144va_end(args);145146efi_puts(printf_buf);147if (printed >= sizeof(printf_buf)) {148efi_puts("[Message truncated]\n");149return -1;150}151152return printed;153}154155156