/src/systemd/src/boot/efi-string.c
Line | Count | Source |
1 | | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2 | | |
3 | | #include "efi-string.h" |
4 | | #include "string-util-fundamental.h" |
5 | | |
6 | | #if SD_BOOT |
7 | | # include "proto/simple-text-io.h" |
8 | | # include "util.h" |
9 | | #else |
10 | | # include <stdlib.h> |
11 | | |
12 | | # include "alloc-util.h" |
13 | 8.76k | # define xnew(t, n) ASSERT_SE_PTR(new(t, n)) |
14 | 0 | # define xmalloc(n) ASSERT_SE_PTR(malloc(n)) |
15 | | #endif |
16 | | |
17 | | /* String functions for both char and char16_t that should behave the same way as their respective |
18 | | * counterpart in userspace. Where it makes sense, these accept NULL and do something sensible whereas |
19 | | * userspace does not allow for this (strlen8(NULL) returns 0 like strlen_ptr(NULL) for example). To make it |
20 | | * easier to tell in code which kind of string they work on, we use 8/16 suffixes. This also makes is easier |
21 | | * to unit test them. */ |
22 | | |
23 | | #define DEFINE_STRNLEN(type, name) \ |
24 | 11.1k | size_t name(const type *s, size_t n) { \ |
25 | 11.1k | if (!s) \ |
26 | 11.1k | return 0; \ |
27 | 11.1k | \ |
28 | 11.1k | size_t len = 0; \ |
29 | 18.8M | while (len < n && *s) { \ |
30 | 18.8M | s++; \ |
31 | 18.8M | len++; \ |
32 | 18.8M | } \ |
33 | 11.1k | \ |
34 | 11.1k | return len; \ |
35 | 11.1k | } Line | Count | Source | 24 | 11.1k | size_t name(const type *s, size_t n) { \ | 25 | 11.1k | if (!s) \ | 26 | 11.1k | return 0; \ | 27 | 11.1k | \ | 28 | 11.1k | size_t len = 0; \ | 29 | 18.8M | while (len < n && *s) { \ | 30 | 18.8M | s++; \ | 31 | 18.8M | len++; \ | 32 | 18.8M | } \ | 33 | 11.1k | \ | 34 | 11.1k | return len; \ | 35 | 11.1k | } |
Unexecuted instantiation: strnlen16 |
36 | | |
37 | | DEFINE_STRNLEN(char, strnlen8); |
38 | | DEFINE_STRNLEN(char16_t, strnlen16); |
39 | | |
40 | | #define TOLOWER(c) \ |
41 | 830k | ({ \ |
42 | 830k | typeof(c) _c = (c); \ |
43 | 830k | (_c >= 'A' && _c <= 'Z') ? _c + ('a' - 'A') : _c; \ |
44 | 830k | }) |
45 | | |
46 | | #define DEFINE_STRTOLOWER(type, name) \ |
47 | 0 | type* name(type *s) { \ |
48 | 0 | if (!s) \ |
49 | 0 | return NULL; \ |
50 | 0 | for (type *p = s; *p; p++) \ |
51 | 0 | *p = TOLOWER(*p); \ |
52 | 0 | return s; \ |
53 | 0 | } |
54 | | |
55 | 0 | DEFINE_STRTOLOWER(char, strtolower8); |
56 | 0 | DEFINE_STRTOLOWER(char16_t, strtolower16); |
57 | | |
58 | | #define DEFINE_STRNCASECMP(type, name, tolower) \ |
59 | 282k | int name(const type *s1, const type *s2, size_t n) { \ |
60 | 282k | if (!s1 || !s2) \ |
61 | 282k | return CMP(s1, s2); \ |
62 | 282k | \ |
63 | 427k | while (n > 0) { \ |
64 | 415k | type c1 = *s1, c2 = *s2; \ |
65 | 415k | if (tolower) { \ |
66 | 415k | c1 = TOLOWER(c1); \ |
67 | 415k | c2 = TOLOWER(c2); \ |
68 | 415k | } \ |
69 | 415k | if (!c1 || c1 != c2) \ |
70 | 415k | return CMP(c1, c2); \ |
71 | 415k | \ |
72 | 415k | s1++; \ |
73 | 144k | s2++; \ |
74 | 144k | n--; \ |
75 | 144k | } \ |
76 | 282k | \ |
77 | 282k | return 0; \ |
78 | 282k | } Unexecuted instantiation: strncmp8 Unexecuted instantiation: strncmp16 Line | Count | Source | 59 | 282k | int name(const type *s1, const type *s2, size_t n) { \ | 60 | 282k | if (!s1 || !s2) \ | 61 | 282k | return CMP(s1, s2); \ | 62 | 282k | \ | 63 | 427k | while (n > 0) { \ | 64 | 415k | type c1 = *s1, c2 = *s2; \ | 65 | 415k | if (tolower) { \ | 66 | 415k | c1 = TOLOWER(c1); \ | 67 | 415k | c2 = TOLOWER(c2); \ | 68 | 415k | } \ | 69 | 415k | if (!c1 || c1 != c2) \ | 70 | 415k | return CMP(c1, c2); \ | 71 | 415k | \ | 72 | 415k | s1++; \ | 73 | 144k | s2++; \ | 74 | 144k | n--; \ | 75 | 144k | } \ | 76 | 282k | \ | 77 | 282k | return 0; \ | 78 | 282k | } |
Unexecuted instantiation: strncasecmp16 |
79 | | |
80 | | DEFINE_STRNCASECMP(char, strncmp8, false); |
81 | | DEFINE_STRNCASECMP(char16_t, strncmp16, false); |
82 | | DEFINE_STRNCASECMP(char, strncasecmp8, true); |
83 | | DEFINE_STRNCASECMP(char16_t, strncasecmp16, true); |
84 | | |
85 | | #define DEFINE_STRCPY(type, name) \ |
86 | 0 | type *name(type * restrict dest, const type * restrict src) { \ |
87 | 0 | type *ret = ASSERT_PTR(dest); \ |
88 | 0 | \ |
89 | 0 | if (!src) { \ |
90 | 0 | *dest = '\0'; \ |
91 | 0 | return ret; \ |
92 | 0 | } \ |
93 | 0 | \ |
94 | 0 | while (*src) { \ |
95 | 0 | *dest = *src; \ |
96 | 0 | dest++; \ |
97 | 0 | src++; \ |
98 | 0 | } \ |
99 | 0 | \ |
100 | 0 | *dest = '\0'; \ |
101 | 0 | return ret; \ |
102 | 0 | } |
103 | | |
104 | 0 | DEFINE_STRCPY(char, strcpy8); |
105 | 0 | DEFINE_STRCPY(char16_t, strcpy16); |
106 | | |
107 | | #define DEFINE_STRCHR(type, name) \ |
108 | 400k | type *name(const type *s, type c) { \ |
109 | 400k | if (!s) \ |
110 | 400k | return NULL; \ |
111 | 400k | \ |
112 | 1.12M | while (*s) { \ |
113 | 770k | if (*s == c) \ |
114 | 770k | return (type *) s; \ |
115 | 770k | s++; \ |
116 | 723k | } \ |
117 | 400k | \ |
118 | 400k | return c ? NULL : (type *) s; \ |
119 | 400k | } |
120 | | |
121 | 400k | DEFINE_STRCHR(char, strchr8); |
122 | 0 | DEFINE_STRCHR(char16_t, strchr16); |
123 | | |
124 | | #define DEFINE_STRNDUP(type, name, len_func) \ |
125 | 0 | type *name(const type *s, size_t n) { \ |
126 | 0 | if (!s) \ |
127 | 0 | return NULL; \ |
128 | 0 | \ |
129 | 0 | size_t len = len_func(s, n); \ |
130 | 0 | size_t size = len * sizeof(type); \ |
131 | 0 | \ |
132 | 0 | type *dup = xmalloc(size + sizeof(type)); \ |
133 | 0 | if (size > 0) \ |
134 | 0 | memcpy(dup, s, size); \ |
135 | 0 | dup[len] = '\0'; \ |
136 | 0 | \ |
137 | 0 | return dup; \ |
138 | 0 | } |
139 | | |
140 | 0 | DEFINE_STRNDUP(char, xstrndup8, strnlen8); |
141 | 0 | DEFINE_STRNDUP(char16_t, xstrndup16, strnlen16); |
142 | | |
143 | 0 | static unsigned utf8_to_unichar(const char *utf8, size_t n, char32_t *c) { |
144 | 0 | char32_t unichar; |
145 | 0 | unsigned len; |
146 | |
|
147 | 0 | assert(utf8); |
148 | 0 | assert(c); |
149 | |
|
150 | 0 | if (!(utf8[0] & 0x80)) { |
151 | 0 | *c = utf8[0]; |
152 | 0 | return 1; |
153 | 0 | } else if ((utf8[0] & 0xe0) == 0xc0) { |
154 | 0 | len = 2; |
155 | 0 | unichar = utf8[0] & 0x1f; |
156 | 0 | } else if ((utf8[0] & 0xf0) == 0xe0) { |
157 | 0 | len = 3; |
158 | 0 | unichar = utf8[0] & 0x0f; |
159 | 0 | } else if ((utf8[0] & 0xf8) == 0xf0) { |
160 | 0 | len = 4; |
161 | 0 | unichar = utf8[0] & 0x07; |
162 | 0 | } else if ((utf8[0] & 0xfc) == 0xf8) { |
163 | 0 | len = 5; |
164 | 0 | unichar = utf8[0] & 0x03; |
165 | 0 | } else if ((utf8[0] & 0xfe) == 0xfc) { |
166 | 0 | len = 6; |
167 | 0 | unichar = utf8[0] & 0x01; |
168 | 0 | } else { |
169 | 0 | *c = UINT32_MAX; |
170 | 0 | return 1; |
171 | 0 | } |
172 | | |
173 | 0 | if (len > n) { |
174 | 0 | *c = UINT32_MAX; |
175 | 0 | return len; |
176 | 0 | } |
177 | | |
178 | 0 | for (unsigned i = 1; i < len; i++) { |
179 | 0 | if ((utf8[i] & 0xc0) != 0x80) { |
180 | 0 | *c = UINT32_MAX; |
181 | 0 | return len; |
182 | 0 | } |
183 | 0 | unichar <<= 6; |
184 | 0 | unichar |= utf8[i] & 0x3f; |
185 | 0 | } |
186 | | |
187 | 0 | *c = unichar; |
188 | 0 | return len; |
189 | 0 | } |
190 | | |
191 | | /* Convert UTF-8 to UCS-2, skipping any invalid or short byte sequences. */ |
192 | 0 | char16_t *xstrn8_to_16(const char *str8, size_t n) { |
193 | 0 | assert(str8 || n == 0); |
194 | |
|
195 | 0 | if (n == SIZE_MAX) |
196 | 0 | n = strlen8(str8); |
197 | |
|
198 | 0 | size_t i = 0; |
199 | 0 | char16_t *str16 = xnew(char16_t, n + 1); |
200 | |
|
201 | 0 | while (n > 0 && *str8 != '\0') { |
202 | 0 | char32_t unichar; |
203 | |
|
204 | 0 | size_t utf8len = utf8_to_unichar(str8, n, &unichar); |
205 | 0 | str8 += utf8len; |
206 | 0 | n = LESS_BY(n, utf8len); |
207 | |
|
208 | 0 | switch (unichar) { |
209 | 0 | case 0 ... 0xd7ffU: |
210 | 0 | case 0xe000U ... 0xffffU: |
211 | 0 | str16[i++] = unichar; |
212 | 0 | break; |
213 | 0 | } |
214 | 0 | } |
215 | | |
216 | 0 | str16[i] = u'\0'; |
217 | 0 | return str16; |
218 | 0 | } |
219 | | |
220 | 0 | char *xstrn16_to_ascii(const char16_t *str16, size_t n) { |
221 | 0 | assert(str16 || n == 0); |
222 | |
|
223 | 0 | if (n == SIZE_MAX) |
224 | 0 | n = strlen16(str16); |
225 | |
|
226 | 0 | _cleanup_free_ char *str8 = xnew(char, n + 1); |
227 | |
|
228 | 0 | size_t i = 0; |
229 | 0 | while (n > 0 && *str16 != u'\0') { |
230 | |
|
231 | 0 | if ((uint16_t) *str16 > 127U) /* Not ASCII? Fail! */ |
232 | 0 | return NULL; |
233 | | |
234 | 0 | str8[i++] = (char) (uint16_t) *str16; |
235 | |
|
236 | 0 | str16++; |
237 | 0 | n--; |
238 | 0 | } |
239 | | |
240 | 0 | str8[i] = '\0'; |
241 | 0 | return TAKE_PTR(str8); |
242 | 0 | } |
243 | | |
244 | 0 | char* startswith8(const char *s, const char *prefix) { |
245 | 0 | size_t l; |
246 | |
|
247 | 0 | assert(prefix); |
248 | |
|
249 | 0 | if (!s) |
250 | 0 | return NULL; |
251 | | |
252 | 0 | l = strlen8(prefix); |
253 | 0 | if (!strneq8(s, prefix, l)) |
254 | 0 | return NULL; |
255 | | |
256 | 0 | return (char*) s + l; |
257 | 0 | } |
258 | | |
259 | 4.99k | static bool efi_fnmatch_prefix(const char16_t *p, const char16_t *h, const char16_t **ret_p, const char16_t **ret_h) { |
260 | 4.99k | assert(p); |
261 | 4.99k | assert(h); |
262 | 4.99k | assert(ret_p); |
263 | 4.99k | assert(ret_h); |
264 | | |
265 | 19.0k | for (;; p++, h++) |
266 | 24.0k | switch (*p) { |
267 | 734 | case '\0': |
268 | | /* End of pattern. Check that haystack is now empty. */ |
269 | 734 | return *h == '\0'; |
270 | | |
271 | 706 | case '\\': |
272 | 706 | p++; |
273 | 706 | if (*p == '\0' || *p != *h) |
274 | | /* Trailing escape or no match. */ |
275 | 506 | return false; |
276 | 200 | break; |
277 | | |
278 | 9.26k | case '?': |
279 | 9.26k | if (*h == '\0') |
280 | | /* Early end of haystack. */ |
281 | 207 | return false; |
282 | 9.05k | break; |
283 | | |
284 | 9.05k | case '*': |
285 | | /* Point ret_p at the remainder of the pattern. */ |
286 | 1.05k | while (*p == '*') |
287 | 629 | p++; |
288 | 428 | *ret_p = p; |
289 | 428 | *ret_h = h; |
290 | 428 | return true; |
291 | | |
292 | 11.7k | case '[': |
293 | 11.7k | if (*h == '\0') |
294 | | /* Early end of haystack. */ |
295 | 337 | return false; |
296 | | |
297 | 11.7k | bool first = true, can_range = true, match = false; |
298 | 37.0k | for (;; first = false) { |
299 | 37.0k | p++; |
300 | 37.0k | if (*p == '\0') |
301 | 1.12k | return false; |
302 | | |
303 | 35.8k | if (*p == '\\') { |
304 | 661 | p++; |
305 | 661 | if (*p == '\0') |
306 | 210 | return false; |
307 | 451 | if (*p == *h) |
308 | 194 | match = true; |
309 | 451 | can_range = true; |
310 | 451 | continue; |
311 | 661 | } |
312 | | |
313 | | /* End of set unless it's the first char. */ |
314 | 35.2k | if (*p == ']' && !first) |
315 | 9.76k | break; |
316 | | |
317 | | /* Range pattern if '-' is not first or last in set. */ |
318 | 25.4k | if (*p == '-' && can_range && !first && *(p + 1) != ']') { |
319 | 1.65k | char16_t low = *(p - 1); |
320 | 1.65k | p++; |
321 | 1.65k | if (*p == '\\') |
322 | 215 | p++; |
323 | 1.65k | if (*p == '\0') |
324 | 289 | return false; |
325 | | |
326 | 1.36k | if (low <= *h && *h <= *p) |
327 | 417 | match = true; |
328 | | |
329 | | /* Ranges cannot be chained: [a-c-f] == [-abcf] */ |
330 | 1.36k | can_range = false; |
331 | 1.36k | continue; |
332 | 1.65k | } |
333 | | |
334 | 23.8k | if (*p == *h) |
335 | 10.5k | match = true; |
336 | 23.8k | can_range = true; |
337 | 23.8k | } |
338 | | |
339 | 9.76k | if (!match) |
340 | 324 | return false; |
341 | 9.43k | break; |
342 | | |
343 | 9.43k | default: |
344 | 1.18k | if (*p != *h) |
345 | | /* Single char mismatch. */ |
346 | 833 | return false; |
347 | 24.0k | } |
348 | 4.99k | } |
349 | | |
350 | | /* Patterns are fnmatch-compatible (with reduced feature support). */ |
351 | 619 | bool efi_fnmatch(const char16_t *pattern, const char16_t *haystack) { |
352 | 619 | assert(haystack); |
353 | | |
354 | | /* Patterns can be considered as simple patterns (without '*') concatenated by '*'. By doing so we |
355 | | * simply have to make sure the very first simple pattern matches the start of haystack. Then we just |
356 | | * look for the remaining simple patterns *somewhere* within the haystack (in order) as any extra |
357 | | * characters in between would be matches by the '*'. We then only have to ensure that the very last |
358 | | * simple pattern matches at the actual end of the haystack. |
359 | | * |
360 | | * This means we do not need to use backtracking which could have catastrophic runtimes with the |
361 | | * right input data. */ |
362 | | |
363 | 4.99k | for (bool first = true;;) { |
364 | 4.99k | const char16_t *pattern_tail = NULL, *haystack_tail = NULL; |
365 | 4.99k | bool match = efi_fnmatch_prefix(pattern, haystack, &pattern_tail, &haystack_tail); |
366 | 4.99k | if (first) { |
367 | 619 | if (!match) |
368 | | /* Initial simple pattern must match. */ |
369 | 299 | return false; |
370 | 320 | if (!pattern_tail) |
371 | | /* No '*' was in pattern, we can return early. */ |
372 | 78 | return true; |
373 | 242 | first = false; |
374 | 242 | } |
375 | | |
376 | 4.61k | if (pattern_tail) { |
377 | 428 | assert(match); |
378 | 428 | pattern = pattern_tail; |
379 | 428 | haystack = haystack_tail; |
380 | 4.18k | } else { |
381 | | /* If we have a match this must be at the end of the haystack. Note that |
382 | | * efi_fnmatch_prefix compares the NUL-bytes at the end, so we cannot match the end |
383 | | * of pattern in the middle of haystack). */ |
384 | 4.18k | if (match || *haystack == '\0') |
385 | 242 | return match; |
386 | | |
387 | | /* Match one character using '*'. */ |
388 | 3.94k | haystack++; |
389 | 3.94k | } |
390 | 4.61k | } |
391 | 619 | } |
392 | | |
393 | | #define DEFINE_PARSE_NUMBER(type, name) \ |
394 | 1.90k | bool name(const type *s, uint64_t *ret_u, const type **ret_tail) { \ |
395 | 1.90k | assert(ret_u); \ |
396 | 1.90k | \ |
397 | 1.90k | if (!s) \ |
398 | 1.90k | return false; \ |
399 | 1.90k | \ |
400 | 1.90k | /* Need at least one digit. */ \ |
401 | 1.90k | if (*s < '0' || *s > '9') \ |
402 | 1.90k | return false; \ |
403 | 1.90k | \ |
404 | 1.90k | uint64_t u = 0; \ |
405 | 4.77k | while (*s >= '0' && *s <= '9') { \ |
406 | 3.95k | if (!MUL_ASSIGN_SAFE(&u, 10)) \ |
407 | 3.95k | return false; \ |
408 | 3.95k | if (!INC_SAFE(&u, *s - '0')) \ |
409 | 3.94k | return false; \ |
410 | 3.94k | s++; \ |
411 | 3.94k | } \ |
412 | 828 | \ |
413 | 828 | if (!ret_tail && *s != '\0') \ |
414 | 825 | return false; \ |
415 | 825 | \ |
416 | 825 | *ret_u = u; \ |
417 | 765 | if (ret_tail) \ |
418 | 765 | *ret_tail = s; \ |
419 | 765 | return true; \ |
420 | 825 | } |
421 | | |
422 | 1.28k | DEFINE_PARSE_NUMBER(char, parse_number8); |
423 | 619 | DEFINE_PARSE_NUMBER(char16_t, parse_number16); |
424 | | |
425 | 0 | bool parse_boolean(const char *v, bool *ret) { |
426 | 0 | assert(ret); |
427 | |
|
428 | 0 | if (!v) |
429 | 0 | return false; |
430 | | |
431 | 0 | if (streq8(v, "1") || streq8(v, "yes") || streq8(v, "y") || streq8(v, "true") || streq8(v, "t") || |
432 | 0 | streq8(v, "on")) { |
433 | 0 | *ret = true; |
434 | 0 | return true; |
435 | 0 | } |
436 | | |
437 | 0 | if (streq8(v, "0") || streq8(v, "no") || streq8(v, "n") || streq8(v, "false") || streq8(v, "f") || |
438 | 0 | streq8(v, "off")) { |
439 | 0 | *ret = false; |
440 | 0 | return true; |
441 | 0 | } |
442 | | |
443 | 0 | return false; |
444 | 0 | } |
445 | | |
446 | 7.60k | char* line_get_key_value(char *s, const char *sep, size_t *pos, char **ret_key, char **ret_value) { |
447 | 7.60k | char *line, *value; |
448 | 7.60k | size_t linelen; |
449 | | |
450 | 7.60k | assert(s); |
451 | 7.60k | assert(sep); |
452 | 7.60k | assert(pos); |
453 | 7.60k | assert(ret_key); |
454 | 7.60k | assert(ret_value); |
455 | | |
456 | 30.7k | for (;;) { |
457 | 30.7k | line = s + *pos; |
458 | 30.7k | if (*line == '\0') |
459 | 213 | return NULL; |
460 | | |
461 | 30.5k | linelen = 0; |
462 | 243k | while (line[linelen] && !strchr8("\n\r", line[linelen])) |
463 | 213k | linelen++; |
464 | | |
465 | | /* move pos to next line */ |
466 | 30.5k | *pos += linelen; |
467 | 30.5k | if (s[*pos]) |
468 | 30.3k | (*pos)++; |
469 | | |
470 | | /* empty line */ |
471 | 30.5k | if (linelen == 0) |
472 | 5.18k | continue; |
473 | | |
474 | | /* terminate line */ |
475 | 25.3k | line[linelen] = '\0'; |
476 | | |
477 | | /* remove leading whitespace */ |
478 | 27.8k | while (linelen > 0 && strchr8(" \t", *line)) { |
479 | 2.52k | line++; |
480 | 2.52k | linelen--; |
481 | 2.52k | } |
482 | | |
483 | | /* remove trailing whitespace */ |
484 | 26.2k | while (linelen > 0 && strchr8(" \t", line[linelen - 1])) |
485 | 895 | linelen--; |
486 | 25.3k | line[linelen] = '\0'; |
487 | | |
488 | 25.3k | if (*line == '#') |
489 | 11.1k | continue; |
490 | | |
491 | | /* split key/value */ |
492 | 14.1k | value = line; |
493 | 96.9k | while (*value && !strchr8(sep, *value)) |
494 | 82.8k | value++; |
495 | 14.1k | if (*value == '\0') |
496 | 6.77k | continue; |
497 | 7.38k | *value = '\0'; |
498 | 7.38k | value++; |
499 | 9.54k | while (*value && strchr8(sep, *value)) |
500 | 2.15k | value++; |
501 | | |
502 | | /* unquote */ |
503 | 7.38k | if (strchr8(QUOTES, value[0]) && line[linelen - 1] == value[0]) { |
504 | 2.03k | value++; |
505 | 2.03k | line[linelen - 1] = '\0'; |
506 | 2.03k | } |
507 | | |
508 | 7.38k | *ret_key = line; |
509 | 7.38k | *ret_value = value; |
510 | 7.38k | return line; |
511 | 14.1k | } |
512 | 7.60k | } |
513 | | |
514 | | char16_t *hexdump(const void *data, size_t size) { |
515 | | const char *hex = LOWERCASE_HEXDIGITS; |
516 | | const uint8_t *d = data; |
517 | | |
518 | | assert(data || size == 0); |
519 | | |
520 | | char16_t *buf = xnew(char16_t, size * 2 + 1); |
521 | | |
522 | | for (size_t i = 0; i < size; i++) { |
523 | | buf[i * 2] = hex[d[i] >> 4]; |
524 | | buf[i * 2 + 1] = hex[d[i] & 0x0F]; |
525 | | } |
526 | | |
527 | | buf[size * 2] = 0; |
528 | | return buf; |
529 | | } |
530 | | |
531 | | static const char * const warn_table[] = { |
532 | | [EFI_SUCCESS] = "Success", |
533 | | [EFI_WARN_UNKNOWN_GLYPH] = "Unknown glyph", |
534 | | [EFI_WARN_DELETE_FAILURE] = "Delete failure", |
535 | | [EFI_WARN_WRITE_FAILURE] = "Write failure", |
536 | | [EFI_WARN_BUFFER_TOO_SMALL] = "Buffer too small", |
537 | | [EFI_WARN_STALE_DATA] = "Stale data", |
538 | | [EFI_WARN_FILE_SYSTEM] = "File system", |
539 | | [EFI_WARN_RESET_REQUIRED] = "Reset required", |
540 | | }; |
541 | | |
542 | | /* Errors have MSB set, remove it to keep the table compact. */ |
543 | 28 | #define NOERR(err) ((err) & ~EFI_ERROR_MASK) |
544 | | |
545 | | static const char * const err_table[] = { |
546 | | [NOERR(EFI_ERROR_MASK)] = "Error", |
547 | | [NOERR(EFI_LOAD_ERROR)] = "Load error", |
548 | | [NOERR(EFI_INVALID_PARAMETER)] = "Invalid parameter", |
549 | | [NOERR(EFI_UNSUPPORTED)] = "Unsupported", |
550 | | [NOERR(EFI_BAD_BUFFER_SIZE)] = "Bad buffer size", |
551 | | [NOERR(EFI_BUFFER_TOO_SMALL)] = "Buffer too small", |
552 | | [NOERR(EFI_NOT_READY)] = "Not ready", |
553 | | [NOERR(EFI_DEVICE_ERROR)] = "Device error", |
554 | | [NOERR(EFI_WRITE_PROTECTED)] = "Write protected", |
555 | | [NOERR(EFI_OUT_OF_RESOURCES)] = "Out of resources", |
556 | | [NOERR(EFI_VOLUME_CORRUPTED)] = "Volume corrupt", |
557 | | [NOERR(EFI_VOLUME_FULL)] = "Volume full", |
558 | | [NOERR(EFI_NO_MEDIA)] = "No media", |
559 | | [NOERR(EFI_MEDIA_CHANGED)] = "Media changed", |
560 | | [NOERR(EFI_NOT_FOUND)] = "Not found", |
561 | | [NOERR(EFI_ACCESS_DENIED)] = "Access denied", |
562 | | [NOERR(EFI_NO_RESPONSE)] = "No response", |
563 | | [NOERR(EFI_NO_MAPPING)] = "No mapping", |
564 | | [NOERR(EFI_TIMEOUT)] = "Time out", |
565 | | [NOERR(EFI_NOT_STARTED)] = "Not started", |
566 | | [NOERR(EFI_ALREADY_STARTED)] = "Already started", |
567 | | [NOERR(EFI_ABORTED)] = "Aborted", |
568 | | [NOERR(EFI_ICMP_ERROR)] = "ICMP error", |
569 | | [NOERR(EFI_TFTP_ERROR)] = "TFTP error", |
570 | | [NOERR(EFI_PROTOCOL_ERROR)] = "Protocol error", |
571 | | [NOERR(EFI_INCOMPATIBLE_VERSION)] = "Incompatible version", |
572 | | [NOERR(EFI_SECURITY_VIOLATION)] = "Security violation", |
573 | | [NOERR(EFI_CRC_ERROR)] = "CRC error", |
574 | | [NOERR(EFI_END_OF_MEDIA)] = "End of media", |
575 | | [NOERR(EFI_ERROR_RESERVED_29)] = "Reserved (29)", |
576 | | [NOERR(EFI_ERROR_RESERVED_30)] = "Reserved (30)", |
577 | | [NOERR(EFI_END_OF_FILE)] = "End of file", |
578 | | [NOERR(EFI_INVALID_LANGUAGE)] = "Invalid language", |
579 | | [NOERR(EFI_COMPROMISED_DATA)] = "Compromised data", |
580 | | [NOERR(EFI_IP_ADDRESS_CONFLICT)] = "IP address conflict", |
581 | | [NOERR(EFI_HTTP_ERROR)] = "HTTP error", |
582 | | }; |
583 | | |
584 | 333 | static const char *status_to_string(EFI_STATUS status) { |
585 | 333 | if (status <= ELEMENTSOF(warn_table) - 1) |
586 | 14 | return warn_table[status]; |
587 | 319 | if (status >= EFI_ERROR_MASK && status <= ((ELEMENTSOF(err_table) - 1) | EFI_ERROR_MASK)) |
588 | 28 | return err_table[NOERR(status)]; |
589 | 291 | return NULL; |
590 | 319 | } |
591 | | |
592 | | typedef struct { |
593 | | size_t padded_len; /* Field width in printf. */ |
594 | | size_t len; /* Precision in printf. */ |
595 | | bool pad_zero; |
596 | | bool align_left; |
597 | | bool alternative_form; |
598 | | bool long_arg; |
599 | | bool longlong_arg; |
600 | | bool have_field_width; |
601 | | |
602 | | const char *str; |
603 | | const wchar_t *wstr; |
604 | | |
605 | | /* For numbers. */ |
606 | | bool is_signed; |
607 | | bool lowercase; |
608 | | int8_t base; |
609 | | char sign_pad; /* For + and (space) flags. */ |
610 | | } SpecifierContext; |
611 | | |
612 | | typedef struct { |
613 | | char16_t stack_buf[128]; /* We use stack_buf first to avoid allocations in most cases. */ |
614 | | char16_t *dyn_buf; /* Allocated buf or NULL if stack_buf is used. */ |
615 | | char16_t *buf; /* Points to the current active buf. */ |
616 | | size_t n_buf; /* Len of buf (in char16_t's, not bytes!). */ |
617 | | size_t n; /* Used len of buf (in char16_t's). This is always <n_buf. */ |
618 | | |
619 | | EFI_STATUS status; |
620 | | const char *format; |
621 | | va_list ap; |
622 | | } FormatContext; |
623 | | |
624 | 9.78k | static void grow_buf(FormatContext *ctx, size_t need) { |
625 | 9.78k | assert(ctx); |
626 | | |
627 | 9.78k | assert_se(INC_SAFE(&need, ctx->n)); |
628 | | |
629 | 9.78k | if (need < ctx->n_buf) |
630 | 3.91k | return; |
631 | | |
632 | | /* Greedily allocate if we can. */ |
633 | 5.86k | if (!MUL_SAFE(&ctx->n_buf, need, 2)) |
634 | 0 | ctx->n_buf = need; |
635 | | |
636 | | /* We cannot use realloc here as ctx->buf may be ctx->stack_buf, which we cannot free. */ |
637 | 5.86k | char16_t *new_buf = xnew(char16_t, ctx->n_buf); |
638 | 5.86k | memcpy(new_buf, ctx->buf, ctx->n * sizeof(*ctx->buf)); |
639 | | |
640 | 5.86k | free(ctx->dyn_buf); |
641 | 5.86k | ctx->buf = ctx->dyn_buf = new_buf; |
642 | 5.86k | } |
643 | | |
644 | 16.4k | static void push_padding(FormatContext *ctx, char pad, size_t len) { |
645 | 16.4k | assert(ctx); |
646 | 111M | while (len > 0) { |
647 | 111M | len--; |
648 | 111M | ctx->buf[ctx->n++] = pad; |
649 | 111M | } |
650 | 16.4k | } |
651 | | |
652 | 3.08k | static bool push_str(FormatContext *ctx, SpecifierContext *sp) { |
653 | 3.08k | assert(ctx); |
654 | 3.08k | assert(sp); |
655 | | |
656 | 3.08k | sp->padded_len = LESS_BY(sp->padded_len, sp->len); |
657 | | |
658 | 3.08k | grow_buf(ctx, sp->padded_len + sp->len); |
659 | | |
660 | 3.08k | if (!sp->align_left) |
661 | 2.54k | push_padding(ctx, ' ', sp->padded_len); |
662 | | |
663 | | /* In userspace unit tests we cannot just memcpy() the wide string. */ |
664 | 3.08k | if (sp->wstr && sizeof(wchar_t) == sizeof(char16_t)) { |
665 | 0 | memcpy(ctx->buf + ctx->n, sp->wstr, sp->len * sizeof(*sp->wstr)); |
666 | 0 | ctx->n += sp->len; |
667 | 3.08k | } else { |
668 | 3.08k | assert(sp->str || sp->wstr); |
669 | 24.1M | for (size_t i = 0; i < sp->len; i++) |
670 | 24.1M | ctx->buf[ctx->n++] = sp->str ? sp->str[i] : sp->wstr[i]; |
671 | 3.08k | } |
672 | | |
673 | 3.08k | if (sp->align_left) |
674 | 540 | push_padding(ctx, ' ', sp->padded_len); |
675 | | |
676 | 3.08k | assert(ctx->n < ctx->n_buf); |
677 | 3.08k | return true; |
678 | 3.08k | } |
679 | | |
680 | 7.23k | static bool push_num(FormatContext *ctx, SpecifierContext *sp, uint64_t u) { |
681 | 7.23k | const char *digits = sp->lowercase ? LOWERCASE_HEXDIGITS : UPPERCASE_HEXDIGITS; |
682 | 7.23k | char16_t tmp[32]; |
683 | 7.23k | size_t n = 0; |
684 | | |
685 | 7.23k | assert(ctx); |
686 | 7.23k | assert(sp); |
687 | 7.23k | assert(IN_SET(sp->base, 10, 16)); |
688 | | |
689 | | /* "%.0u" prints nothing if value is 0. */ |
690 | 7.23k | if (u == 0 && sp->len == 0) |
691 | 543 | return true; |
692 | | |
693 | 6.69k | if (sp->is_signed && (int64_t) u < 0) { |
694 | | /* We cannot just do "u = -(int64_t)u" here because -INT64_MIN overflows. */ |
695 | | |
696 | 1.53k | uint64_t rem = -((int64_t) u % sp->base); |
697 | 1.53k | u = (int64_t) u / -sp->base; |
698 | 1.53k | tmp[n++] = digits[rem]; |
699 | 1.53k | sp->sign_pad = '-'; |
700 | 1.53k | } |
701 | | |
702 | 75.6k | while (u > 0 || n == 0) { |
703 | 68.9k | uint64_t rem = u % sp->base; |
704 | 68.9k | u /= sp->base; |
705 | 68.9k | tmp[n++] = digits[rem]; |
706 | 68.9k | } |
707 | | |
708 | | /* Note that numbers never get truncated! */ |
709 | 6.69k | size_t prefix = (sp->sign_pad != 0 ? 1 : 0) + (sp->alternative_form ? 2 : 0); |
710 | 6.69k | size_t number_len = prefix + MAX(n, sp->len); |
711 | 6.69k | grow_buf(ctx, MAX(sp->padded_len, number_len)); |
712 | | |
713 | 6.69k | size_t padding = 0; |
714 | 6.69k | if (sp->pad_zero) |
715 | | /* Leading zeroes go after the sign or 0x prefix. */ |
716 | 999 | number_len = MAX(number_len, sp->padded_len); |
717 | 5.69k | else |
718 | 5.69k | padding = LESS_BY(sp->padded_len, number_len); |
719 | | |
720 | 6.69k | if (!sp->align_left) |
721 | 2.99k | push_padding(ctx, ' ', padding); |
722 | | |
723 | 6.69k | if (sp->sign_pad != 0) |
724 | 2.04k | ctx->buf[ctx->n++] = sp->sign_pad; |
725 | 6.69k | if (sp->alternative_form) { |
726 | 1.21k | ctx->buf[ctx->n++] = '0'; |
727 | 1.21k | ctx->buf[ctx->n++] = sp->lowercase ? 'x' : 'X'; |
728 | 1.21k | } |
729 | | |
730 | 6.69k | push_padding(ctx, '0', LESS_BY(number_len, n + prefix)); |
731 | | |
732 | 77.1k | while (n > 0) |
733 | 70.4k | ctx->buf[ctx->n++] = tmp[--n]; |
734 | | |
735 | 6.69k | if (sp->align_left) |
736 | 3.70k | push_padding(ctx, ' ', padding); |
737 | | |
738 | 6.69k | assert(ctx->n < ctx->n_buf); |
739 | 6.69k | return true; |
740 | 7.23k | } |
741 | | |
742 | | /* This helps unit testing. */ |
743 | | #if SD_BOOT |
744 | | # define NULLSTR "(null)" |
745 | | # define wcsnlen strnlen16 |
746 | | #else |
747 | 49 | # define NULLSTR "(nil)" |
748 | | #endif |
749 | | |
750 | 40.9k | static bool handle_format_specifier(FormatContext *ctx, SpecifierContext *sp) { |
751 | | /* Parses one item from the format specifier in ctx and put the info into sp. If we are done with |
752 | | * this specifier returns true, otherwise this function should be called again. */ |
753 | | |
754 | | /* This implementation assumes 32-bit ints. Also note that all types smaller than int are promoted to |
755 | | * int in vararg functions, which is why we fetch only ints for any such types. The compiler would |
756 | | * otherwise warn about fetching smaller types. */ |
757 | 40.9k | assert_cc(sizeof(int) == 4); |
758 | 40.9k | assert_cc(sizeof(wchar_t) <= sizeof(int)); |
759 | 40.9k | assert_cc(sizeof(long long) == sizeof(intmax_t)); |
760 | | |
761 | 40.9k | assert(ctx); |
762 | 40.9k | assert(sp); |
763 | | |
764 | 40.9k | switch (*ctx->format) { |
765 | 666 | case '#': |
766 | 666 | sp->alternative_form = true; |
767 | 666 | return false; |
768 | 6.66k | case '.': |
769 | 6.66k | sp->have_field_width = true; |
770 | 6.66k | return false; |
771 | 666 | case '-': |
772 | 666 | sp->align_left = true; |
773 | 666 | return false; |
774 | 333 | case '+': |
775 | 999 | case ' ': |
776 | 999 | sp->sign_pad = *ctx->format; |
777 | 999 | return false; |
778 | | |
779 | 999 | case '0': |
780 | 999 | if (!sp->have_field_width) { |
781 | 999 | sp->pad_zero = true; |
782 | 999 | return false; |
783 | 999 | } |
784 | | |
785 | | /* If field width has already been provided then 0 is part of precision (%.0s). */ |
786 | 0 | _fallthrough_; |
787 | |
|
788 | 14.9k | case '*': |
789 | 15.6k | case '1' ... '9': { |
790 | 15.6k | int64_t i; |
791 | | |
792 | 15.6k | if (*ctx->format == '*') |
793 | 14.9k | i = va_arg(ctx->ap, int); |
794 | 666 | else { |
795 | 666 | uint64_t u; |
796 | 666 | if (!parse_number8(ctx->format, &u, &ctx->format) || u > INT_MAX) |
797 | 0 | assert_not_reached(); |
798 | 666 | ctx->format--; /* Point it back to the last digit. */ |
799 | 666 | i = u; |
800 | 666 | } |
801 | | |
802 | 15.6k | if (sp->have_field_width) { |
803 | | /* Negative precision is ignored. */ |
804 | 6.66k | if (i >= 0) |
805 | 4.46k | sp->len = (size_t) i; |
806 | 8.99k | } else { |
807 | | /* Negative field width is treated as positive field width with '-' flag. */ |
808 | 8.99k | if (i < 0) { |
809 | 4.17k | i *= -1; |
810 | 4.17k | sp->align_left = true; |
811 | 4.17k | } |
812 | 8.99k | sp->padded_len = i; |
813 | 8.99k | } |
814 | | |
815 | 15.6k | return false; |
816 | 14.9k | } |
817 | | |
818 | 1.99k | case 'h': |
819 | 1.99k | if (*(ctx->format + 1) == 'h') |
820 | 999 | ctx->format++; |
821 | | /* char/short gets promoted to int, nothing to do here. */ |
822 | 1.99k | return false; |
823 | | |
824 | 2.33k | case 'l': |
825 | 2.33k | if (*(ctx->format + 1) == 'l') { |
826 | 999 | ctx->format++; |
827 | 999 | sp->longlong_arg = true; |
828 | 999 | } else |
829 | 1.33k | sp->long_arg = true; |
830 | 2.33k | return false; |
831 | | |
832 | 666 | case 'z': |
833 | 666 | sp->long_arg = sizeof(size_t) == sizeof(long); |
834 | 666 | sp->longlong_arg = !sp->long_arg && sizeof(size_t) == sizeof(long long); |
835 | 666 | return false; |
836 | | |
837 | 666 | case 'j': |
838 | 666 | sp->long_arg = sizeof(intmax_t) == sizeof(long); |
839 | 666 | sp->longlong_arg = !sp->long_arg && sizeof(intmax_t) == sizeof(long long); |
840 | 666 | return false; |
841 | | |
842 | 333 | case 't': |
843 | 333 | sp->long_arg = sizeof(ptrdiff_t) == sizeof(long); |
844 | 333 | sp->longlong_arg = !sp->long_arg && sizeof(ptrdiff_t) == sizeof(long long); |
845 | 333 | return false; |
846 | | |
847 | 333 | case '%': |
848 | 333 | sp->str = "%"; |
849 | 333 | sp->len = 1; |
850 | 333 | return push_str(ctx, sp); |
851 | | |
852 | 999 | case 'c': |
853 | 999 | sp->wstr = &(wchar_t){ va_arg(ctx->ap, int) }; |
854 | 999 | sp->len = 1; |
855 | 999 | return push_str(ctx, sp); |
856 | | |
857 | 666 | case 's': |
858 | 666 | if (sp->long_arg) { |
859 | 333 | sp->wstr = va_arg(ctx->ap, const wchar_t *) ?: L"(null)"; |
860 | 333 | sp->len = wcsnlen(sp->wstr, sp->len); |
861 | 333 | } else { |
862 | 333 | sp->str = va_arg(ctx->ap, const char *) ?: "(null)"; |
863 | 333 | sp->len = strnlen8(sp->str, sp->len); |
864 | 333 | } |
865 | 666 | return push_str(ctx, sp); |
866 | | |
867 | 0 | case 'd': |
868 | 3.99k | case 'i': |
869 | 5.66k | case 'u': |
870 | 6.32k | case 'x': |
871 | 6.66k | case 'X': |
872 | 6.66k | sp->lowercase = *ctx->format == 'x'; |
873 | 6.66k | sp->is_signed = IN_SET(*ctx->format, 'd', 'i'); |
874 | 6.66k | sp->base = IN_SET(*ctx->format, 'x', 'X') ? 16 : 10; |
875 | 6.66k | if (sp->len == SIZE_MAX) |
876 | 3.07k | sp->len = 1; |
877 | | |
878 | 6.66k | uint64_t v; |
879 | 6.66k | if (sp->longlong_arg) |
880 | 999 | v = sp->is_signed ? (uint64_t) va_arg(ctx->ap, long long) : |
881 | 999 | va_arg(ctx->ap, unsigned long long); |
882 | 5.66k | else if (sp->long_arg) |
883 | 2.66k | v = sp->is_signed ? (uint64_t) va_arg(ctx->ap, long) : va_arg(ctx->ap, unsigned long); |
884 | 2.99k | else |
885 | 2.99k | v = sp->is_signed ? (uint64_t) va_arg(ctx->ap, int) : va_arg(ctx->ap, unsigned); |
886 | | |
887 | 6.66k | return push_num(ctx, sp, v); |
888 | | |
889 | 333 | case 'p': { |
890 | 333 | const void *ptr = va_arg(ctx->ap, const void *); |
891 | 333 | if (!ptr) { |
892 | 49 | sp->str = NULLSTR; |
893 | 49 | sp->len = STRLEN(NULLSTR); |
894 | 49 | return push_str(ctx, sp); |
895 | 49 | } |
896 | | |
897 | 284 | sp->base = 16; |
898 | 284 | sp->lowercase = true; |
899 | 284 | sp->alternative_form = true; |
900 | 284 | sp->len = 0; /* Precision is ignored for %p. */ |
901 | 284 | return push_num(ctx, sp, (uintptr_t) ptr); |
902 | 333 | } |
903 | | |
904 | 333 | case 'm': { |
905 | 333 | sp->str = status_to_string(ctx->status); |
906 | 333 | if (sp->str) { |
907 | 42 | sp->len = strlen8(sp->str); |
908 | 42 | return push_str(ctx, sp); |
909 | 42 | } |
910 | | |
911 | 291 | sp->base = 16; |
912 | 291 | sp->lowercase = true; |
913 | 291 | sp->alternative_form = true; |
914 | 291 | sp->len = 0; |
915 | 291 | return push_num(ctx, sp, ctx->status); |
916 | 333 | } |
917 | | |
918 | 0 | default: |
919 | 0 | assert_not_reached(); |
920 | 40.9k | } |
921 | 40.9k | } |
922 | | |
923 | | #if SD_BOOT |
924 | | static void output_string_safe(EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *this, const char16_t *s) { |
925 | | assert(this); |
926 | | assert(s); |
927 | | |
928 | | /* This is a color-conscious version of ST->ConOut->OutputString(). Whenever it encounters a newline |
929 | | * character, it will reset the color to our default, because some UEFI implementations/terminals |
930 | | * reset the color in that case, and we want our default color to remain in effect */ |
931 | | |
932 | | int32_t saved_attribute = ST->ConOut->Mode->Attribute; |
933 | | for (;;) { |
934 | | const char16_t *nl = strchr16(s, '\n'); |
935 | | if (!nl) /* No further newline */ |
936 | | return (void) ST->ConOut->OutputString(ST->ConOut, (char16_t*) s); |
937 | | |
938 | | if (nl[1] == 0) { /* Newline is at the end of the string */ |
939 | | (void) ST->ConOut->OutputString(ST->ConOut, (char16_t*) s); |
940 | | set_attribute_safe(saved_attribute); |
941 | | return; |
942 | | } |
943 | | |
944 | | /* newline is in the middle of the string */ |
945 | | _cleanup_free_ char16_t *x = xstrndup16(s, nl - s + 1); |
946 | | (void) ST->ConOut->OutputString(ST->ConOut, x); |
947 | | set_attribute_safe(saved_attribute); |
948 | | |
949 | | s = nl + 1; |
950 | | } |
951 | | } |
952 | | #endif |
953 | | |
954 | | /* printf_internal is largely compatible to userspace vasprintf. Any features omitted should trigger asserts. |
955 | | * |
956 | | * Supported: |
957 | | * - Flags: #, 0, +, -, space |
958 | | * - Lengths: h, hh, l, ll, z, j, t |
959 | | * - Specifiers: %, c, s, u, i, d, x, X, p, m |
960 | | * - Precision and width (inline or as int arg using *) |
961 | | * |
962 | | * Notable differences: |
963 | | * - Passing NULL to %s is permitted and will print "(null)" |
964 | | * - %p will also use "(null)" |
965 | | * - The provided EFI_STATUS is used for %m instead of errno |
966 | | * - "\n" is translated to "\r\n" */ |
967 | 8.32k | _printf_(2, 0) static char16_t *printf_internal(EFI_STATUS status, const char *format, va_list ap, bool ret) { |
968 | 8.32k | assert(format); |
969 | | |
970 | 8.32k | FormatContext ctx = { |
971 | 8.32k | .buf = ctx.stack_buf, |
972 | 8.32k | .n_buf = ELEMENTSOF(ctx.stack_buf), |
973 | 8.32k | .format = format, |
974 | 8.32k | .status = status, |
975 | 8.32k | }; |
976 | | |
977 | | /* We cannot put this into the struct without making a copy. */ |
978 | 8.32k | va_copy(ctx.ap, ap); |
979 | | |
980 | 18.6k | while (*ctx.format != '\0') { |
981 | 10.3k | SpecifierContext sp = { .len = SIZE_MAX }; |
982 | | |
983 | 10.3k | switch (*ctx.format) { |
984 | 9.32k | case '%': |
985 | 9.32k | ctx.format++; |
986 | 40.9k | while (!handle_format_specifier(&ctx, &sp)) |
987 | 31.6k | ctx.format++; |
988 | 9.32k | ctx.format++; |
989 | 9.32k | break; |
990 | 0 | case '\n': |
991 | 0 | ctx.format++; |
992 | 0 | sp.str = "\r\n"; |
993 | 0 | sp.len = 2; |
994 | 0 | push_str(&ctx, &sp); |
995 | 0 | break; |
996 | 999 | default: |
997 | 999 | sp.str = ctx.format++; |
998 | 999 | while (!IN_SET(*ctx.format, '%', '\n', '\0')) |
999 | 0 | ctx.format++; |
1000 | 999 | sp.len = ctx.format - sp.str; |
1001 | 999 | push_str(&ctx, &sp); |
1002 | 10.3k | } |
1003 | 10.3k | } |
1004 | | |
1005 | 8.32k | va_end(ctx.ap); |
1006 | | |
1007 | 8.32k | assert(ctx.n < ctx.n_buf); |
1008 | 8.32k | ctx.buf[ctx.n++] = '\0'; |
1009 | | |
1010 | 8.32k | if (ret) { |
1011 | 8.32k | if (ctx.dyn_buf) |
1012 | 5.42k | return TAKE_PTR(ctx.dyn_buf); |
1013 | | |
1014 | 2.90k | char16_t *ret_buf = xnew(char16_t, ctx.n); |
1015 | 2.90k | memcpy(ret_buf, ctx.buf, ctx.n * sizeof(*ctx.buf)); |
1016 | 2.90k | return ret_buf; |
1017 | 8.32k | } |
1018 | | |
1019 | | #if SD_BOOT |
1020 | | output_string_safe(ST->ConOut, ctx.buf); |
1021 | | #endif |
1022 | | |
1023 | 0 | return mfree(ctx.dyn_buf); |
1024 | 8.32k | } |
1025 | | |
1026 | 0 | void printf_status(EFI_STATUS status, const char *format, ...) { |
1027 | 0 | va_list ap; |
1028 | 0 | va_start(ap, format); |
1029 | 0 | printf_internal(status, format, ap, false); |
1030 | 0 | va_end(ap); |
1031 | 0 | } |
1032 | | |
1033 | 0 | void vprintf_status(EFI_STATUS status, const char *format, va_list ap) { |
1034 | 0 | printf_internal(status, format, ap, false); |
1035 | 0 | } |
1036 | | |
1037 | 8.32k | char16_t *xasprintf_status(EFI_STATUS status, const char *format, ...) { |
1038 | 8.32k | va_list ap; |
1039 | 8.32k | va_start(ap, format); |
1040 | 8.32k | char16_t *ret = printf_internal(status, format, ap, true); |
1041 | 8.32k | va_end(ap); |
1042 | 8.32k | return ret; |
1043 | 8.32k | } |
1044 | | |
1045 | 0 | char16_t *xvasprintf_status(EFI_STATUS status, const char *format, va_list ap) { |
1046 | 0 | return printf_internal(status, format, ap, true); |
1047 | 0 | } |
1048 | | |
1049 | | #if SD_BOOT |
1050 | | /* To provide the actual implementation for these we need to remove the redirection to the builtins. */ |
1051 | | # undef memchr |
1052 | | # undef memcmp |
1053 | | # undef memcpy |
1054 | | # undef memset |
1055 | | // NOLINTBEGIN(misc-use-internal-linkage) |
1056 | | _used_ void *memchr(const void *p, int c, size_t n); |
1057 | | _used_ int memcmp(const void *p1, const void *p2, size_t n); |
1058 | | _used_ void *memcpy(void * restrict dest, const void * restrict src, size_t n); |
1059 | | _used_ void *memset(void *p, int c, size_t n); |
1060 | | // NOLINTEND(misc-use-internal-linkage) |
1061 | | #else |
1062 | | /* And for userspace unit testing we need to give them an efi_ prefix. */ |
1063 | | # undef memchr |
1064 | | # define memchr efi_memchr |
1065 | | # define memcmp efi_memcmp |
1066 | | # define memcpy efi_memcpy |
1067 | | # define memset efi_memset |
1068 | | #endif |
1069 | | |
1070 | 0 | void *memchr(const void *p, int c, size_t n) { |
1071 | 0 | if (!p || n == 0) |
1072 | 0 | return NULL; |
1073 | | |
1074 | 0 | const uint8_t *q = p; |
1075 | 0 | for (size_t i = 0; i < n; i++) |
1076 | 0 | if (q[i] == (unsigned char) c) |
1077 | 0 | return (void *) (q + i); |
1078 | | |
1079 | 0 | return NULL; |
1080 | 0 | } |
1081 | | |
1082 | 0 | int memcmp(const void *p1, const void *p2, size_t n) { |
1083 | 0 | const uint8_t *up1 = p1, *up2 = p2; |
1084 | 0 | int r; |
1085 | |
|
1086 | 0 | if (!p1 || !p2) |
1087 | 0 | return CMP(p1, p2); |
1088 | | |
1089 | 0 | while (n > 0) { |
1090 | 0 | r = CMP(*up1, *up2); |
1091 | 0 | if (r != 0) |
1092 | 0 | return r; |
1093 | | |
1094 | 0 | up1++; |
1095 | 0 | up2++; |
1096 | 0 | n--; |
1097 | 0 | } |
1098 | | |
1099 | 0 | return 0; |
1100 | 0 | } |
1101 | | |
1102 | 0 | void *memcpy(void * restrict dest, const void * restrict src, size_t n) { |
1103 | 0 | if (!dest || !src || n == 0) |
1104 | 0 | return dest; |
1105 | | |
1106 | | #if SD_BOOT |
1107 | | /* The firmware-provided memcpy is likely optimized, so use that. The function is guaranteed to be |
1108 | | * available by the UEFI spec. We still make it depend on the boot services pointer being set just in |
1109 | | * case the compiler emits a call before it is available. */ |
1110 | | if (_likely_(BS)) { |
1111 | | BS->CopyMem(dest, (void *) src, n); |
1112 | | return dest; |
1113 | | } |
1114 | | #endif |
1115 | | |
1116 | 0 | uint8_t *d = dest; |
1117 | 0 | const uint8_t *s = src; |
1118 | |
|
1119 | 0 | while (n > 0) { |
1120 | 0 | *d = *s; |
1121 | 0 | d++; |
1122 | 0 | s++; |
1123 | 0 | n--; |
1124 | 0 | } |
1125 | |
|
1126 | 0 | return dest; |
1127 | 0 | } |
1128 | | |
1129 | 0 | void *memset(void *p, int c, size_t n) { |
1130 | 0 | if (!p || n == 0) |
1131 | 0 | return p; |
1132 | | |
1133 | | #if SD_BOOT |
1134 | | /* See comment in efi_memcpy. Note that the signature has c and n swapped! */ |
1135 | | if (_likely_(BS)) { |
1136 | | BS->SetMem(p, n, c); |
1137 | | return p; |
1138 | | } |
1139 | | #endif |
1140 | | |
1141 | 0 | uint8_t *q = p; |
1142 | 0 | while (n > 0) { |
1143 | 0 | *q = c; |
1144 | 0 | q++; |
1145 | 0 | n--; |
1146 | 0 | } |
1147 | |
|
1148 | 0 | return p; |
1149 | 0 | } |
1150 | | |
1151 | 0 | size_t strspn16(const char16_t *p, const char16_t *good) { |
1152 | 0 | assert(p); |
1153 | 0 | assert(good); |
1154 | |
|
1155 | 0 | const char16_t *i = p; |
1156 | 0 | for (; *i != 0; i++) |
1157 | 0 | if (!strchr16(good, *i)) |
1158 | 0 | break; |
1159 | |
|
1160 | 0 | return i - p; |
1161 | 0 | } |
1162 | | |
1163 | 0 | size_t strcspn16(const char16_t *p, const char16_t *bad) { |
1164 | 0 | assert(p); |
1165 | 0 | assert(bad); |
1166 | |
|
1167 | 0 | const char16_t *i = p; |
1168 | 0 | for (; *i != 0; i++) |
1169 | 0 | if (strchr16(bad, *i)) |
1170 | 0 | break; |
1171 | |
|
1172 | 0 | return i - p; |
1173 | 0 | } |