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