]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/boot/efi/util.c
efi: NUL terminate strings read with efivar_get()
[thirdparty/systemd.git] / src / boot / efi / util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
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 */
13 const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} };
14
15 #ifdef __x86_64__
16 UINT64 ticks_read(VOID) {
17 UINT64 a, d;
18 __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d));
19 return (d << 32) | a;
20 }
21 #elif defined(__i386__)
22 UINT64 ticks_read(VOID) {
23 UINT64 val;
24 __asm__ volatile ("rdtsc" : "=A" (val));
25 return val;
26 }
27 #else
28 UINT64 ticks_read(VOID) {
29 UINT64 val = 1;
30 return val;
31 }
32 #endif
33
34 /* count TSC ticks during a millisecond delay */
35 UINT64 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
42 return (ticks_end - ticks_start) * 1000;
43 }
44
45 UINT64 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
59 return 1000 * 1000 * ticks / freq;
60 }
61
62 EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b) {
63 if (!v)
64 return EFI_INVALID_PARAMETER;
65
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
85 EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, const CHAR16 *name, const VOID *buf, UINTN size, BOOLEAN persistent) {
86 UINT32 flags;
87
88 flags = EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
89 if (persistent)
90 flags |= EFI_VARIABLE_NON_VOLATILE;
91
92 return uefi_call_wrapper(RT->SetVariable, 5, (CHAR16*) name, (EFI_GUID *)vendor, flags, size, (VOID*) buf);
93 }
94
95 EFI_STATUS efivar_set(const CHAR16 *name, const CHAR16 *value, BOOLEAN persistent) {
96 return efivar_set_raw(&loader_guid, name, value, value ? (StrLen(value)+1) * sizeof(CHAR16) : 0, persistent);
97 }
98
99 EFI_STATUS efivar_set_int(CHAR16 *name, UINTN i, BOOLEAN persistent) {
100 CHAR16 str[32];
101
102 SPrint(str, 32, L"%u", i);
103 return efivar_set(name, str, persistent);
104 }
105
106 EFI_STATUS efivar_get(const CHAR16 *name, CHAR16 **value) {
107 _cleanup_freepool_ CHAR8 *buf = NULL;
108 EFI_STATUS err;
109 CHAR16 *val;
110 UINTN size;
111
112 err = efivar_get_raw(&loader_guid, name, &buf, &size);
113 if (EFI_ERROR(err))
114 return err;
115
116 /* Make sure there are no incomplete characters in the buffer */
117 if ((size % 2) != 0)
118 return EFI_INVALID_PARAMETER;
119
120 /* Return buffer directly if it happens to be NUL terminated already */
121 if (size >= 2 && buf[size-2] == 0 && buf[size-1] == 0) {
122 *value = (CHAR16*) buf;
123 buf = NULL;
124 return EFI_SUCCESS;
125 }
126
127 /* Make sure a terminating NUL is available at the end */
128 val = AllocatePool(size + 2);
129 if (!val)
130 return EFI_OUT_OF_RESOURCES;
131
132 CopyMem(val, buf, size);
133 val[size/2] = 0; /* NUL terminate */
134
135 *value = val;
136 return EFI_SUCCESS;
137 }
138
139 EFI_STATUS efivar_get_int(const CHAR16 *name, UINTN *i) {
140 _cleanup_freepool_ CHAR16 *val = NULL;
141 EFI_STATUS err;
142
143 err = efivar_get(name, &val);
144 if (!EFI_ERROR(err))
145 *i = Atoi(val);
146
147 return err;
148 }
149
150 EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const CHAR16 *name, CHAR8 **buffer, UINTN *size) {
151 _cleanup_freepool_ CHAR8 *buf = NULL;
152 UINTN l;
153 EFI_STATUS err;
154
155 l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
156 buf = AllocatePool(l);
157 if (!buf)
158 return EFI_OUT_OF_RESOURCES;
159
160 err = uefi_call_wrapper(RT->GetVariable, 5, (CHAR16*) name, (EFI_GUID *)vendor, NULL, &l, buf);
161 if (!EFI_ERROR(err)) {
162 *buffer = buf;
163 buf = NULL;
164 if (size)
165 *size = l;
166 }
167
168 return err;
169 }
170
171 VOID efivar_set_time_usec(CHAR16 *name, UINT64 usec) {
172 CHAR16 str[32];
173
174 if (usec == 0)
175 usec = time_usec();
176 if (usec == 0)
177 return;
178
179 SPrint(str, 32, L"%ld", usec);
180 efivar_set(name, str, FALSE);
181 }
182
183 static INTN utf8_to_16(CHAR8 *stra, CHAR16 *c) {
184 CHAR16 unichar;
185 UINTN len;
186 UINTN i;
187
188 if (stra[0] < 0x80)
189 len = 1;
190 else if ((stra[0] & 0xe0) == 0xc0)
191 len = 2;
192 else if ((stra[0] & 0xf0) == 0xe0)
193 len = 3;
194 else if ((stra[0] & 0xf8) == 0xf0)
195 len = 4;
196 else if ((stra[0] & 0xfc) == 0xf8)
197 len = 5;
198 else if ((stra[0] & 0xfe) == 0xfc)
199 len = 6;
200 else
201 return -1;
202
203 switch (len) {
204 case 1:
205 unichar = stra[0];
206 break;
207 case 2:
208 unichar = stra[0] & 0x1f;
209 break;
210 case 3:
211 unichar = stra[0] & 0x0f;
212 break;
213 case 4:
214 unichar = stra[0] & 0x07;
215 break;
216 case 5:
217 unichar = stra[0] & 0x03;
218 break;
219 case 6:
220 unichar = stra[0] & 0x01;
221 break;
222 }
223
224 for (i = 1; i < len; i++) {
225 if ((stra[i] & 0xc0) != 0x80)
226 return -1;
227 unichar <<= 6;
228 unichar |= stra[i] & 0x3f;
229 }
230
231 *c = unichar;
232 return len;
233 }
234
235 CHAR16 *stra_to_str(CHAR8 *stra) {
236 UINTN strlen;
237 UINTN len;
238 UINTN i;
239 CHAR16 *str;
240
241 len = strlena(stra);
242 str = AllocatePool((len + 1) * sizeof(CHAR16));
243
244 strlen = 0;
245 i = 0;
246 while (i < len) {
247 INTN utf8len;
248
249 utf8len = utf8_to_16(stra + i, str + strlen);
250 if (utf8len <= 0) {
251 /* invalid utf8 sequence, skip the garbage */
252 i++;
253 continue;
254 }
255
256 strlen++;
257 i += utf8len;
258 }
259 str[strlen] = '\0';
260 return str;
261 }
262
263 CHAR16 *stra_to_path(CHAR8 *stra) {
264 CHAR16 *str;
265 UINTN strlen;
266 UINTN len;
267 UINTN i;
268
269 len = strlena(stra);
270 str = AllocatePool((len + 2) * sizeof(CHAR16));
271
272 str[0] = '\\';
273 strlen = 1;
274 i = 0;
275 while (i < len) {
276 INTN utf8len;
277
278 utf8len = utf8_to_16(stra + i, str + strlen);
279 if (utf8len <= 0) {
280 /* invalid utf8 sequence, skip the garbage */
281 i++;
282 continue;
283 }
284
285 if (str[strlen] == '/')
286 str[strlen] = '\\';
287 if (str[strlen] == '\\' && str[strlen-1] == '\\') {
288 /* skip double slashes */
289 i += utf8len;
290 continue;
291 }
292
293 strlen++;
294 i += utf8len;
295 }
296 str[strlen] = '\0';
297 return str;
298 }
299
300 CHAR8 *strchra(CHAR8 *s, CHAR8 c) {
301 do {
302 if (*s == c)
303 return s;
304 } while (*s++);
305 return NULL;
306 }
307
308 EFI_STATUS file_read(EFI_FILE_HANDLE dir, const CHAR16 *name, UINTN off, UINTN size, CHAR8 **content, UINTN *content_size) {
309 EFI_FILE_HANDLE handle;
310 _cleanup_freepool_ CHAR8 *buf = NULL;
311 EFI_STATUS err;
312
313 err = uefi_call_wrapper(dir->Open, 5, dir, &handle, (CHAR16*) name, EFI_FILE_MODE_READ, 0ULL);
314 if (EFI_ERROR(err))
315 return err;
316
317 if (size == 0) {
318 _cleanup_freepool_ EFI_FILE_INFO *info;
319
320 info = LibFileInfo(handle);
321 size = info->FileSize+1;
322 }
323
324 if (off > 0) {
325 err = uefi_call_wrapper(handle->SetPosition, 2, handle, off);
326 if (EFI_ERROR(err))
327 return err;
328 }
329
330 buf = AllocatePool(size + 1);
331 err = uefi_call_wrapper(handle->Read, 3, handle, &size, buf);
332 if (!EFI_ERROR(err)) {
333 buf[size] = '\0';
334 *content = buf;
335 buf = NULL;
336 if (content_size)
337 *content_size = size;
338 }
339 uefi_call_wrapper(handle->Close, 1, handle);
340
341 return err;
342 }