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