1 // SPDX-License-Identifier: GPL-2.0 2 3 #include <linux/stdarg.h> 4 5 #include <linux/ctype.h> 6 #include <linux/efi.h> 7 #include <linux/kernel.h> 8 #include <linux/printk.h> /* For CONSOLE_LOGLEVEL_* */ 9 #include <asm/efi.h> 10 #include <asm/setup.h> 11 12 #include "efistub.h" 13 14 int efi_loglevel = CONSOLE_LOGLEVEL_DEFAULT; 15 16 /** 17 * efi_char16_puts() - Write a UCS-2 encoded string to the console 18 * @str: UCS-2 encoded string 19 */ 20 void efi_char16_puts(efi_char16_t *str) 21 { 22 efi_call_proto(efi_table_attr(efi_system_table, con_out), 23 output_string, str); 24 } 25 26 static 27 u32 utf8_to_utf32(const u8 **s8) 28 { 29 u32 c32; 30 u8 c0, cx; 31 size_t clen, i; 32 33 c0 = cx = *(*s8)++; 34 /* 35 * The position of the most-significant 0 bit gives us the length of 36 * a multi-octet encoding. 37 */ 38 for (clen = 0; cx & 0x80; ++clen) 39 cx <<= 1; 40 /* 41 * If the 0 bit is in position 8, this is a valid single-octet 42 * encoding. If the 0 bit is in position 7 or positions 1-3, the 43 * encoding is invalid. 44 * In either case, we just return the first octet. 45 */ 46 if (clen < 2 || clen > 4) 47 return c0; 48 /* Get the bits from the first octet. */ 49 c32 = cx >> clen--; 50 for (i = 0; i < clen; ++i) { 51 /* Trailing octets must have 10 in most significant bits. */ 52 cx = (*s8)[i] ^ 0x80; 53 if (cx & 0xc0) 54 return c0; 55 c32 = (c32 << 6) | cx; 56 } 57 /* 58 * Check for validity: 59 * - The character must be in the Unicode range. 60 * - It must not be a surrogate. 61 * - It must be encoded using the correct number of octets. 62 */ 63 if (c32 > 0x10ffff || 64 (c32 & 0xf800) == 0xd800 || 65 clen != (c32 >= 0x80) + (c32 >= 0x800) + (c32 >= 0x10000)) 66 return c0; 67 *s8 += clen; 68 return c32; 69 } 70 71 /** 72 * efi_puts() - Write a UTF-8 encoded string to the console 73 * @str: UTF-8 encoded string 74 */ 75 void efi_puts(const char *str) 76 { 77 efi_char16_t buf[128]; 78 size_t pos = 0, lim = ARRAY_SIZE(buf); 79 const u8 *s8 = (const u8 *)str; 80 u32 c32; 81 82 while (*s8) { 83 if (*s8 == '\n') 84 buf[pos++] = L'\r'; 85 c32 = utf8_to_utf32(&s8); 86 if (c32 < 0x10000) { 87 /* Characters in plane 0 use a single word. */ 88 buf[pos++] = c32; 89 } else { 90 /* 91 * Characters in other planes encode into a surrogate 92 * pair. 93 */ 94 buf[pos++] = (0xd800 - (0x10000 >> 10)) + (c32 >> 10); 95 buf[pos++] = 0xdc00 + (c32 & 0x3ff); 96 } 97 if (*s8 == '\0' || pos >= lim - 2) { 98 buf[pos] = L'\0'; 99 efi_char16_puts(buf); 100 pos = 0; 101 } 102 } 103 } 104 105 /** 106 * efi_printk() - Print a kernel message 107 * @fmt: format string 108 * 109 * The first letter of the format string is used to determine the logging level 110 * of the message. If the level is less then the current EFI logging level, the 111 * message is suppressed. The message will be truncated to 255 bytes. 112 * 113 * Return: number of printed characters 114 */ 115 int efi_printk(const char *fmt, ...) 116 { 117 char printf_buf[256]; 118 va_list args; 119 int printed; 120 int loglevel = printk_get_level(fmt); 121 122 switch (loglevel) { 123 case '0' ... '9': 124 loglevel -= '0'; 125 break; 126 default: 127 /* 128 * Use loglevel -1 for cases where we just want to print to 129 * the screen. 130 */ 131 loglevel = -1; 132 break; 133 } 134 135 if (loglevel >= efi_loglevel) 136 return 0; 137 138 if (loglevel >= 0) 139 efi_puts("EFI stub: "); 140 141 fmt = printk_skip_level(fmt); 142 143 va_start(args, fmt); 144 printed = vsnprintf(printf_buf, sizeof(printf_buf), fmt, args); 145 va_end(args); 146 147 efi_puts(printf_buf); 148 if (printed >= sizeof(printf_buf)) { 149 efi_puts("[Message truncated]\n"); 150 return -1; 151 } 152 153 return printed; 154 } 155