]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/boot/efi/util.c
sd-boot: Unify error handling
[thirdparty/systemd.git] / src / boot / efi / util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <efi.h>
4 #include <efilib.h>
5
6 #include "util.h"
7
8 #ifdef __x86_64__
9 UINT64 ticks_read(VOID) {
10 UINT64 a, d;
11 __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d));
12 return (d << 32) | a;
13 }
14 #elif defined(__i386__)
15 UINT64 ticks_read(VOID) {
16 UINT64 val;
17 __asm__ volatile ("rdtsc" : "=A" (val));
18 return val;
19 }
20 #elif defined(__aarch64__)
21 UINT64 ticks_read(VOID) {
22 UINT64 val;
23 __asm__ volatile ("mrs %0, cntpct_el0" : "=r" (val));
24 return val;
25 }
26 #else
27 UINT64 ticks_read(VOID) {
28 UINT64 val = 1;
29 return val;
30 }
31 #endif
32
33 #if defined(__aarch64__)
34 UINT64 ticks_freq(VOID) {
35 UINT64 freq;
36 __asm__ volatile ("mrs %0, cntfrq_el0": "=r" (freq));
37 return freq;
38 }
39 #else
40 /* count TSC ticks during a millisecond delay */
41 UINT64 ticks_freq(VOID) {
42 UINT64 ticks_start, ticks_end;
43
44 ticks_start = ticks_read();
45 uefi_call_wrapper(BS->Stall, 1, 1000);
46 ticks_end = ticks_read();
47
48 return (ticks_end - ticks_start) * 1000UL;
49 }
50 #endif
51
52 UINT64 time_usec(VOID) {
53 UINT64 ticks;
54 static UINT64 freq;
55
56 ticks = ticks_read();
57 if (ticks == 0)
58 return 0;
59
60 if (freq == 0) {
61 freq = ticks_freq();
62 if (freq == 0)
63 return 0;
64 }
65
66 return 1000UL * 1000UL * ticks / freq;
67 }
68
69 EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b) {
70 if (!v)
71 return EFI_INVALID_PARAMETER;
72
73 if (strcmpa(v, (CHAR8 *)"1") == 0 ||
74 strcmpa(v, (CHAR8 *)"yes") == 0 ||
75 strcmpa(v, (CHAR8 *)"y") == 0 ||
76 strcmpa(v, (CHAR8 *)"true") == 0) {
77 *b = TRUE;
78 return EFI_SUCCESS;
79 }
80
81 if (strcmpa(v, (CHAR8 *)"0") == 0 ||
82 strcmpa(v, (CHAR8 *)"no") == 0 ||
83 strcmpa(v, (CHAR8 *)"n") == 0 ||
84 strcmpa(v, (CHAR8 *)"false") == 0) {
85 *b = FALSE;
86 return EFI_SUCCESS;
87 }
88
89 return EFI_INVALID_PARAMETER;
90 }
91
92 EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, const CHAR16 *name, const VOID *buf, UINTN size, UINT32 flags) {
93 flags |= EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;
94 return uefi_call_wrapper(RT->SetVariable, 5, (CHAR16*) name, (EFI_GUID *)vendor, flags, size, (VOID*) buf);
95 }
96
97 EFI_STATUS efivar_set(const EFI_GUID *vendor, const CHAR16 *name, const CHAR16 *value, UINT32 flags) {
98 return efivar_set_raw(vendor, name, value, value ? (StrLen(value) + 1) * sizeof(CHAR16) : 0, flags);
99 }
100
101 EFI_STATUS efivar_set_uint_string(const EFI_GUID *vendor, const CHAR16 *name, UINTN i, UINT32 flags) {
102 CHAR16 str[32];
103
104 SPrint(str, 32, L"%u", i);
105 return efivar_set(vendor, name, str, flags);
106 }
107
108 EFI_STATUS efivar_set_uint32_le(const EFI_GUID *vendor, const CHAR16 *name, UINT32 value, UINT32 flags) {
109 UINT8 buf[4];
110
111 buf[0] = (UINT8)(value >> 0U & 0xFF);
112 buf[1] = (UINT8)(value >> 8U & 0xFF);
113 buf[2] = (UINT8)(value >> 16U & 0xFF);
114 buf[3] = (UINT8)(value >> 24U & 0xFF);
115
116 return efivar_set_raw(vendor, name, buf, sizeof(buf), flags);
117 }
118
119 EFI_STATUS efivar_set_uint64_le(const EFI_GUID *vendor, const CHAR16 *name, UINT64 value, UINT32 flags) {
120 UINT8 buf[8];
121
122 buf[0] = (UINT8)(value >> 0U & 0xFF);
123 buf[1] = (UINT8)(value >> 8U & 0xFF);
124 buf[2] = (UINT8)(value >> 16U & 0xFF);
125 buf[3] = (UINT8)(value >> 24U & 0xFF);
126 buf[4] = (UINT8)(value >> 32U & 0xFF);
127 buf[5] = (UINT8)(value >> 40U & 0xFF);
128 buf[6] = (UINT8)(value >> 48U & 0xFF);
129 buf[7] = (UINT8)(value >> 56U & 0xFF);
130
131 return efivar_set_raw(vendor, name, buf, sizeof(buf), flags);
132 }
133
134 EFI_STATUS efivar_get(const EFI_GUID *vendor, const CHAR16 *name, CHAR16 **value) {
135 _cleanup_freepool_ CHAR8 *buf = NULL;
136 EFI_STATUS err;
137 CHAR16 *val;
138 UINTN size;
139
140 err = efivar_get_raw(vendor, name, &buf, &size);
141 if (EFI_ERROR(err))
142 return err;
143
144 /* Make sure there are no incomplete characters in the buffer */
145 if ((size % 2) != 0)
146 return EFI_INVALID_PARAMETER;
147
148 if (!value)
149 return EFI_SUCCESS;
150
151 /* Return buffer directly if it happens to be NUL terminated already */
152 if (size >= 2 && buf[size-2] == 0 && buf[size-1] == 0) {
153 *value = (CHAR16*) TAKE_PTR(buf);
154 return EFI_SUCCESS;
155 }
156
157 /* Make sure a terminating NUL is available at the end */
158 val = AllocatePool(size + 2);
159 if (!val)
160 return EFI_OUT_OF_RESOURCES;
161
162 CopyMem(val, buf, size);
163 val[size/2] = 0; /* NUL terminate */
164
165 *value = val;
166 return EFI_SUCCESS;
167 }
168
169 EFI_STATUS efivar_get_uint_string(const EFI_GUID *vendor, const CHAR16 *name, UINTN *i) {
170 _cleanup_freepool_ CHAR16 *val = NULL;
171 EFI_STATUS err;
172
173 err = efivar_get(vendor, name, &val);
174 if (!EFI_ERROR(err) && i)
175 *i = Atoi(val);
176
177 return err;
178 }
179
180 EFI_STATUS efivar_get_uint32_le(const EFI_GUID *vendor, const CHAR16 *name, UINT32 *ret) {
181 _cleanup_freepool_ CHAR8 *buf = NULL;
182 UINTN size;
183 EFI_STATUS err;
184
185 err = efivar_get_raw(vendor, name, &buf, &size);
186 if (!EFI_ERROR(err) && ret) {
187 if (size != sizeof(UINT32))
188 return EFI_BUFFER_TOO_SMALL;
189
190 *ret = (UINT32) buf[0] << 0U | (UINT32) buf[1] << 8U | (UINT32) buf[2] << 16U |
191 (UINT32) buf[3] << 24U;
192 }
193
194 return err;
195 }
196
197 EFI_STATUS efivar_get_uint64_le(const EFI_GUID *vendor, const CHAR16 *name, UINT64 *ret) {
198 _cleanup_freepool_ CHAR8 *buf = NULL;
199 UINTN size;
200 EFI_STATUS err;
201
202 err = efivar_get_raw(vendor, name, &buf, &size);
203 if (!EFI_ERROR(err) && ret) {
204 if (size != sizeof(UINT64))
205 return EFI_BUFFER_TOO_SMALL;
206
207 *ret = (UINT64) buf[0] << 0U | (UINT64) buf[1] << 8U | (UINT64) buf[2] << 16U |
208 (UINT64) buf[3] << 24U | (UINT64) buf[4] << 32U | (UINT64) buf[5] << 40U |
209 (UINT64) buf[6] << 48U | (UINT64) buf[7] << 56U;
210 }
211
212 return err;
213 }
214
215 EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const CHAR16 *name, CHAR8 **buffer, UINTN *size) {
216 _cleanup_freepool_ CHAR8 *buf = NULL;
217 UINTN l;
218 EFI_STATUS err;
219
220 l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
221 buf = AllocatePool(l);
222 if (!buf)
223 return EFI_OUT_OF_RESOURCES;
224
225 err = uefi_call_wrapper(RT->GetVariable, 5, (CHAR16*) name, (EFI_GUID *)vendor, NULL, &l, buf);
226 if (!EFI_ERROR(err)) {
227
228 if (buffer)
229 *buffer = TAKE_PTR(buf);
230
231 if (size)
232 *size = l;
233 }
234
235 return err;
236 }
237
238 EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const CHAR16 *name, BOOLEAN *ret) {
239 _cleanup_freepool_ CHAR8 *b = NULL;
240 UINTN size;
241 EFI_STATUS err;
242
243 err = efivar_get_raw(vendor, name, &b, &size);
244 if (!EFI_ERROR(err))
245 *ret = *b > 0;
246
247 return err;
248 }
249
250 VOID efivar_set_time_usec(const EFI_GUID *vendor, const CHAR16 *name, UINT64 usec) {
251 CHAR16 str[32];
252
253 if (usec == 0)
254 usec = time_usec();
255 if (usec == 0)
256 return;
257
258 SPrint(str, 32, L"%ld", usec);
259 efivar_set(vendor, name, str, 0);
260 }
261
262 static INTN utf8_to_16(const CHAR8 *stra, CHAR16 *c) {
263 CHAR16 unichar;
264 UINTN len;
265
266 if (!(stra[0] & 0x80))
267 len = 1;
268 else if ((stra[0] & 0xe0) == 0xc0)
269 len = 2;
270 else if ((stra[0] & 0xf0) == 0xe0)
271 len = 3;
272 else if ((stra[0] & 0xf8) == 0xf0)
273 len = 4;
274 else if ((stra[0] & 0xfc) == 0xf8)
275 len = 5;
276 else if ((stra[0] & 0xfe) == 0xfc)
277 len = 6;
278 else
279 return -1;
280
281 switch (len) {
282 case 1:
283 unichar = stra[0];
284 break;
285 case 2:
286 unichar = stra[0] & 0x1f;
287 break;
288 case 3:
289 unichar = stra[0] & 0x0f;
290 break;
291 case 4:
292 unichar = stra[0] & 0x07;
293 break;
294 case 5:
295 unichar = stra[0] & 0x03;
296 break;
297 case 6:
298 unichar = stra[0] & 0x01;
299 break;
300 }
301
302 for (UINTN i = 1; i < len; i++) {
303 if ((stra[i] & 0xc0) != 0x80)
304 return -1;
305 unichar <<= 6;
306 unichar |= stra[i] & 0x3f;
307 }
308
309 *c = unichar;
310 return len;
311 }
312
313 CHAR16 *stra_to_str(const CHAR8 *stra) {
314 UINTN strlen;
315 UINTN len;
316 UINTN i;
317 CHAR16 *str;
318
319 len = strlena(stra);
320 str = AllocatePool((len + 1) * sizeof(CHAR16));
321
322 strlen = 0;
323 i = 0;
324 while (i < len) {
325 INTN utf8len;
326
327 utf8len = utf8_to_16(stra + i, str + strlen);
328 if (utf8len <= 0) {
329 /* invalid utf8 sequence, skip the garbage */
330 i++;
331 continue;
332 }
333
334 strlen++;
335 i += utf8len;
336 }
337 str[strlen] = '\0';
338 return str;
339 }
340
341 CHAR16 *stra_to_path(const CHAR8 *stra) {
342 CHAR16 *str;
343 UINTN strlen;
344 UINTN len;
345 UINTN i;
346
347 len = strlena(stra);
348 str = AllocatePool((len + 2) * sizeof(CHAR16));
349
350 str[0] = '\\';
351 strlen = 1;
352 i = 0;
353 while (i < len) {
354 INTN utf8len;
355
356 utf8len = utf8_to_16(stra + i, str + strlen);
357 if (utf8len <= 0) {
358 /* invalid utf8 sequence, skip the garbage */
359 i++;
360 continue;
361 }
362
363 if (str[strlen] == '/')
364 str[strlen] = '\\';
365 if (str[strlen] == '\\' && str[strlen-1] == '\\') {
366 /* skip double slashes */
367 i += utf8len;
368 continue;
369 }
370
371 strlen++;
372 i += utf8len;
373 }
374 str[strlen] = '\0';
375 return str;
376 }
377
378 CHAR8 *strchra(const CHAR8 *s, CHAR8 c) {
379 do {
380 if (*s == c)
381 return (CHAR8*) s;
382 } while (*s++);
383 return NULL;
384 }
385
386 EFI_STATUS file_read(EFI_FILE_HANDLE dir, const CHAR16 *name, UINTN off, UINTN size, CHAR8 **ret, UINTN *ret_size) {
387 _cleanup_(FileHandleClosep) EFI_FILE_HANDLE handle = NULL;
388 _cleanup_freepool_ CHAR8 *buf = NULL;
389 EFI_STATUS err;
390
391 err = uefi_call_wrapper(dir->Open, 5, dir, &handle, (CHAR16*) name, EFI_FILE_MODE_READ, 0ULL);
392 if (EFI_ERROR(err))
393 return err;
394
395 if (size == 0) {
396 _cleanup_freepool_ EFI_FILE_INFO *info = NULL;
397
398 info = LibFileInfo(handle);
399 if (!info)
400 return EFI_OUT_OF_RESOURCES;
401
402 size = info->FileSize+1;
403 }
404
405 if (off > 0) {
406 err = uefi_call_wrapper(handle->SetPosition, 2, handle, off);
407 if (EFI_ERROR(err))
408 return err;
409 }
410
411 buf = AllocatePool(size + 1);
412 if (!buf)
413 return EFI_OUT_OF_RESOURCES;
414
415 err = uefi_call_wrapper(handle->Read, 3, handle, &size, buf);
416 if (EFI_ERROR(err))
417 return err;
418
419 buf[size] = '\0';
420
421 *ret = TAKE_PTR(buf);
422 if (ret_size)
423 *ret_size = size;
424
425 return err;
426 }
427
428 VOID log_error_stall(const CHAR16 *fmt, ...) {
429 va_list args;
430
431 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTRED|EFI_BACKGROUND_BLACK);
432
433 Print(L"\n");
434 va_start(args, fmt);
435 VPrint(fmt, args);
436 va_end(args);
437 Print(L"\n");
438
439 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
440 }
441
442 EFI_STATUS log_oom(void) {
443 log_error_stall(L"Out of memory.");
444 return EFI_OUT_OF_RESOURCES;
445 }