]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/boot/efi/util.c
efi: add log_oom() helper
[thirdparty/systemd.git] / src / boot / efi / util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
0fa2cac4
KS
2
3#include <efi.h>
4#include <efilib.h>
5
6#include "util.h"
7
8/*
9 * Allocated random UUID, intended to be shared across tools that implement
10 * the (ESP)\loader\entries\<vendor>-<revision>.conf convention and the
11 * associated EFI variables.
12 */
0e2bc732 13const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} };
0fa2cac4
KS
14
15#ifdef __x86_64__
16UINT64 ticks_read(VOID) {
17 UINT64 a, d;
18 __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d));
19 return (d << 32) | a;
20}
8403daa2 21#elif defined(__i386__)
0fa2cac4
KS
22UINT64 ticks_read(VOID) {
23 UINT64 val;
24 __asm__ volatile ("rdtsc" : "=A" (val));
25 return val;
26}
0d8d3689
KK
27#else
28UINT64 ticks_read(VOID) {
29 UINT64 val = 1;
30 return val;
31}
0fa2cac4
KS
32#endif
33
34/* count TSC ticks during a millisecond delay */
35UINT64 ticks_freq(VOID) {
36 UINT64 ticks_start, ticks_end;
37
38 ticks_start = ticks_read();
39 uefi_call_wrapper(BS->Stall, 1, 1000);
40 ticks_end = ticks_read();
41
c027b67f 42 return (ticks_end - ticks_start) * 1000UL;
0fa2cac4
KS
43}
44
45UINT64 time_usec(VOID) {
46 UINT64 ticks;
47 static UINT64 freq;
48
49 ticks = ticks_read();
50 if (ticks == 0)
51 return 0;
52
53 if (freq == 0) {
54 freq = ticks_freq();
55 if (freq == 0)
56 return 0;
57 }
58
c027b67f 59 return 1000UL * 1000UL * ticks / freq;
0fa2cac4
KS
60}
61
427ee7ec 62EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b) {
95a18e91
LP
63 if (!v)
64 return EFI_INVALID_PARAMETER;
65
4db7e6d7
KS
66 if (strcmpa(v, (CHAR8 *)"1") == 0 ||
67 strcmpa(v, (CHAR8 *)"yes") == 0 ||
68 strcmpa(v, (CHAR8 *)"y") == 0 ||
69 strcmpa(v, (CHAR8 *)"true") == 0) {
70 *b = TRUE;
71 return EFI_SUCCESS;
72 }
73
74 if (strcmpa(v, (CHAR8 *)"0") == 0 ||
75 strcmpa(v, (CHAR8 *)"no") == 0 ||
76 strcmpa(v, (CHAR8 *)"n") == 0 ||
77 strcmpa(v, (CHAR8 *)"false") == 0) {
78 *b = FALSE;
79 return EFI_SUCCESS;
80 }
81
82 return EFI_INVALID_PARAMETER;
83}
84
427ee7ec 85EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, const CHAR16 *name, const VOID *buf, UINTN size, BOOLEAN persistent) {
0fa2cac4
KS
86 UINT32 flags;
87
88 flags = EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
89 if (persistent)
90 flags |= EFI_VARIABLE_NON_VOLATILE;
91
427ee7ec 92 return uefi_call_wrapper(RT->SetVariable, 5, (CHAR16*) name, (EFI_GUID *)vendor, flags, size, (VOID*) buf);
0fa2cac4
KS
93}
94
427ee7ec 95EFI_STATUS efivar_set(const CHAR16 *name, const CHAR16 *value, BOOLEAN persistent) {
f82ecab0 96 return efivar_set_raw(&loader_guid, name, value, value ? (StrLen(value)+1) * sizeof(CHAR16) : 0, persistent);
0fa2cac4
KS
97}
98
99EFI_STATUS efivar_set_int(CHAR16 *name, UINTN i, BOOLEAN persistent) {
100 CHAR16 str[32];
101
427ee7ec 102 SPrint(str, 32, L"%u", i);
0fa2cac4
KS
103 return efivar_set(name, str, persistent);
104}
105
427ee7ec 106EFI_STATUS efivar_get(const CHAR16 *name, CHAR16 **value) {
46af0be9 107 _cleanup_freepool_ CHAR8 *buf = NULL;
3b42f349 108 EFI_STATUS err;
0fa2cac4
KS
109 CHAR16 *val;
110 UINTN size;
0fa2cac4
KS
111
112 err = efivar_get_raw(&loader_guid, name, &buf, &size);
113 if (EFI_ERROR(err))
114 return err;
115
3b42f349
LP
116 /* Make sure there are no incomplete characters in the buffer */
117 if ((size % 2) != 0)
118 return EFI_INVALID_PARAMETER;
119
b9e45242
LP
120 if (!value)
121 return EFI_SUCCESS;
122
3b42f349
LP
123 /* Return buffer directly if it happens to be NUL terminated already */
124 if (size >= 2 && buf[size-2] == 0 && buf[size-1] == 0) {
c1db999e 125 *value = (CHAR16*) TAKE_PTR(buf);
3b42f349
LP
126 return EFI_SUCCESS;
127 }
128
129 /* Make sure a terminating NUL is available at the end */
130 val = AllocatePool(size + 2);
46af0be9 131 if (!val)
0fa2cac4 132 return EFI_OUT_OF_RESOURCES;
0fa2cac4 133
3b42f349
LP
134 CopyMem(val, buf, size);
135 val[size/2] = 0; /* NUL terminate */
136
0fa2cac4
KS
137 *value = val;
138 return EFI_SUCCESS;
139}
140
427ee7ec 141EFI_STATUS efivar_get_int(const CHAR16 *name, UINTN *i) {
46af0be9 142 _cleanup_freepool_ CHAR16 *val = NULL;
0fa2cac4
KS
143 EFI_STATUS err;
144
145 err = efivar_get(name, &val);
b9e45242 146 if (!EFI_ERROR(err) && i)
0fa2cac4 147 *i = Atoi(val);
46af0be9 148
0fa2cac4
KS
149 return err;
150}
151
427ee7ec 152EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const CHAR16 *name, CHAR8 **buffer, UINTN *size) {
46af0be9 153 _cleanup_freepool_ CHAR8 *buf = NULL;
0fa2cac4
KS
154 UINTN l;
155 EFI_STATUS err;
156
157 l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
158 buf = AllocatePool(l);
159 if (!buf)
160 return EFI_OUT_OF_RESOURCES;
161
427ee7ec 162 err = uefi_call_wrapper(RT->GetVariable, 5, (CHAR16*) name, (EFI_GUID *)vendor, NULL, &l, buf);
0fa2cac4 163 if (!EFI_ERROR(err)) {
b9e45242
LP
164
165 if (buffer)
166 *buffer = TAKE_PTR(buf);
167
0fa2cac4
KS
168 if (size)
169 *size = l;
46af0be9 170 }
0fa2cac4 171
46af0be9 172 return err;
0fa2cac4
KS
173}
174
175VOID efivar_set_time_usec(CHAR16 *name, UINT64 usec) {
176 CHAR16 str[32];
177
178 if (usec == 0)
179 usec = time_usec();
180 if (usec == 0)
181 return;
182
183 SPrint(str, 32, L"%ld", usec);
184 efivar_set(name, str, FALSE);
185}
186
187static INTN utf8_to_16(CHAR8 *stra, CHAR16 *c) {
188 CHAR16 unichar;
189 UINTN len;
190 UINTN i;
191
192 if (stra[0] < 0x80)
193 len = 1;
194 else if ((stra[0] & 0xe0) == 0xc0)
195 len = 2;
196 else if ((stra[0] & 0xf0) == 0xe0)
197 len = 3;
198 else if ((stra[0] & 0xf8) == 0xf0)
199 len = 4;
200 else if ((stra[0] & 0xfc) == 0xf8)
201 len = 5;
202 else if ((stra[0] & 0xfe) == 0xfc)
203 len = 6;
204 else
205 return -1;
206
207 switch (len) {
208 case 1:
209 unichar = stra[0];
210 break;
211 case 2:
212 unichar = stra[0] & 0x1f;
213 break;
214 case 3:
215 unichar = stra[0] & 0x0f;
216 break;
217 case 4:
218 unichar = stra[0] & 0x07;
219 break;
220 case 5:
221 unichar = stra[0] & 0x03;
222 break;
223 case 6:
224 unichar = stra[0] & 0x01;
225 break;
226 }
227
228 for (i = 1; i < len; i++) {
229 if ((stra[i] & 0xc0) != 0x80)
230 return -1;
231 unichar <<= 6;
232 unichar |= stra[i] & 0x3f;
233 }
234
235 *c = unichar;
236 return len;
237}
238
239CHAR16 *stra_to_str(CHAR8 *stra) {
240 UINTN strlen;
241 UINTN len;
242 UINTN i;
243 CHAR16 *str;
244
245 len = strlena(stra);
246 str = AllocatePool((len + 1) * sizeof(CHAR16));
247
248 strlen = 0;
249 i = 0;
250 while (i < len) {
251 INTN utf8len;
252
253 utf8len = utf8_to_16(stra + i, str + strlen);
254 if (utf8len <= 0) {
255 /* invalid utf8 sequence, skip the garbage */
256 i++;
257 continue;
258 }
259
260 strlen++;
261 i += utf8len;
262 }
263 str[strlen] = '\0';
264 return str;
265}
266
267CHAR16 *stra_to_path(CHAR8 *stra) {
268 CHAR16 *str;
269 UINTN strlen;
270 UINTN len;
271 UINTN i;
272
273 len = strlena(stra);
274 str = AllocatePool((len + 2) * sizeof(CHAR16));
275
276 str[0] = '\\';
277 strlen = 1;
278 i = 0;
279 while (i < len) {
280 INTN utf8len;
281
282 utf8len = utf8_to_16(stra + i, str + strlen);
283 if (utf8len <= 0) {
284 /* invalid utf8 sequence, skip the garbage */
285 i++;
286 continue;
287 }
288
289 if (str[strlen] == '/')
290 str[strlen] = '\\';
291 if (str[strlen] == '\\' && str[strlen-1] == '\\') {
292 /* skip double slashes */
293 i += utf8len;
294 continue;
295 }
296
297 strlen++;
298 i += utf8len;
299 }
300 str[strlen] = '\0';
301 return str;
302}
303
304CHAR8 *strchra(CHAR8 *s, CHAR8 c) {
305 do {
306 if (*s == c)
307 return s;
308 } while (*s++);
309 return NULL;
310}
311
1aaabb17
LP
312EFI_STATUS file_read(EFI_FILE_HANDLE dir, const CHAR16 *name, UINTN off, UINTN size, CHAR8 **ret, UINTN *ret_size) {
313 _cleanup_(FileHandleClosep) EFI_FILE_HANDLE handle = NULL;
46af0be9 314 _cleanup_freepool_ CHAR8 *buf = NULL;
0fa2cac4 315 EFI_STATUS err;
0fa2cac4 316
427ee7ec 317 err = uefi_call_wrapper(dir->Open, 5, dir, &handle, (CHAR16*) name, EFI_FILE_MODE_READ, 0ULL);
0fa2cac4
KS
318 if (EFI_ERROR(err))
319 return err;
320
321 if (size == 0) {
46af0be9 322 _cleanup_freepool_ EFI_FILE_INFO *info;
0fa2cac4
KS
323
324 info = LibFileInfo(handle);
1aaabb17
LP
325 if (!info)
326 return EFI_OUT_OF_RESOURCES;
327
33d4ba32 328 size = info->FileSize+1;
33d4ba32 329 }
0fa2cac4
KS
330
331 if (off > 0) {
332 err = uefi_call_wrapper(handle->SetPosition, 2, handle, off);
333 if (EFI_ERROR(err))
334 return err;
335 }
336
70756b3b 337 buf = AllocatePool(size + 1);
1aaabb17
LP
338 if (!buf)
339 return EFI_OUT_OF_RESOURCES;
340
33d4ba32 341 err = uefi_call_wrapper(handle->Read, 3, handle, &size, buf);
1aaabb17
LP
342 if (EFI_ERROR(err))
343 return err;
344
345 buf[size] = '\0';
346
347 *ret = TAKE_PTR(buf);
348 if (ret_size)
349 *ret_size = size;
46af0be9 350
33d4ba32 351 return err;
0fa2cac4 352}
b19fa812
LP
353
354EFI_STATUS log_oom(void) {
355 Print(L"Out of memory.");
356 (void) uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
357 return EFI_OUT_OF_RESOURCES;
358}