Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/firmware/efi/libstub/printk.c
26483 views
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/kern_levels.h>
9
#include <asm/efi.h>
10
#include <asm/setup.h>
11
12
#include "efistub.h"
13
14
int efi_loglevel = LOGLEVEL_NOTICE;
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
156