brintos

brintos / linux-shallow public Read only

0
0
Text · 3.3 KiB · 3a67a2c Raw
155 lines · c
1// SPDX-License-Identifier: GPL-2.02 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 14int efi_loglevel = CONSOLE_LOGLEVEL_DEFAULT;15 16/**17 * efi_char16_puts() - Write a UCS-2 encoded string to the console18 * @str:	UCS-2 encoded string19 */20void 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 26static27u32 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 of36	 * 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-octet42	 * encoding. If the 0 bit is in position 7 or positions 1-3, the43	 * 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 console73 * @str:	UTF-8 encoded string74 */75void 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 surrogate92			 * 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 message107 * @fmt:	format string108 *109 * The first letter of the format string is used to determine the logging level110 * of the message. If the level is less then the current EFI logging level, the111 * message is suppressed. The message will be truncated to 255 bytes.112 *113 * Return:	number of printed characters114 */115int 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 to129		 * 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