]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/boot/efi/util.c
Merge pull request #2589 from keszybz/resolve-tool-2
[thirdparty/systemd.git] / src / boot / efi / util.c
1 /*
2 * This program is free software; you can redistribute it and/or modify it
3 * under the terms of the GNU Lesser General Public License as published by
4 * the Free Software Foundation; either version 2.1 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful, but
8 * WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * Copyright (C) 2012-2013 Kay Sievers <kay@vrfy.org>
13 * Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
14 */
15
16 #include <efi.h>
17 #include <efilib.h>
18
19 #include "util.h"
20
21 /*
22 * Allocated random UUID, intended to be shared across tools that implement
23 * the (ESP)\loader\entries\<vendor>-<revision>.conf convention and the
24 * associated EFI variables.
25 */
26 static const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} };
27
28 #ifdef __x86_64__
29 UINT64 ticks_read(VOID) {
30 UINT64 a, d;
31 __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d));
32 return (d << 32) | a;
33 }
34 #elif defined(__i386__)
35 UINT64 ticks_read(VOID) {
36 UINT64 val;
37 __asm__ volatile ("rdtsc" : "=A" (val));
38 return val;
39 }
40 #else
41 UINT64 ticks_read(VOID) {
42 UINT64 val = 1;
43 return val;
44 }
45 #endif
46
47 /* count TSC ticks during a millisecond delay */
48 UINT64 ticks_freq(VOID) {
49 UINT64 ticks_start, ticks_end;
50
51 ticks_start = ticks_read();
52 uefi_call_wrapper(BS->Stall, 1, 1000);
53 ticks_end = ticks_read();
54
55 return (ticks_end - ticks_start) * 1000;
56 }
57
58 UINT64 time_usec(VOID) {
59 UINT64 ticks;
60 static UINT64 freq;
61
62 ticks = ticks_read();
63 if (ticks == 0)
64 return 0;
65
66 if (freq == 0) {
67 freq = ticks_freq();
68 if (freq == 0)
69 return 0;
70 }
71
72 return 1000 * 1000 * ticks / freq;
73 }
74
75 EFI_STATUS parse_boolean(CHAR8 *v, BOOLEAN *b) {
76 if (strcmpa(v, (CHAR8 *)"1") == 0 ||
77 strcmpa(v, (CHAR8 *)"yes") == 0 ||
78 strcmpa(v, (CHAR8 *)"y") == 0 ||
79 strcmpa(v, (CHAR8 *)"true") == 0) {
80 *b = TRUE;
81 return EFI_SUCCESS;
82 }
83
84 if (strcmpa(v, (CHAR8 *)"0") == 0 ||
85 strcmpa(v, (CHAR8 *)"no") == 0 ||
86 strcmpa(v, (CHAR8 *)"n") == 0 ||
87 strcmpa(v, (CHAR8 *)"false") == 0) {
88 *b = FALSE;
89 return EFI_SUCCESS;
90 }
91
92 return EFI_INVALID_PARAMETER;
93 }
94
95 EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent) {
96 UINT32 flags;
97
98 flags = EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
99 if (persistent)
100 flags |= EFI_VARIABLE_NON_VOLATILE;
101
102 return uefi_call_wrapper(RT->SetVariable, 5, name, (EFI_GUID *)vendor, flags, size, buf);
103 }
104
105 EFI_STATUS efivar_set(CHAR16 *name, CHAR16 *value, BOOLEAN persistent) {
106 return efivar_set_raw(&loader_guid, name, (CHAR8 *)value, value ? (StrLen(value)+1) * sizeof(CHAR16) : 0, persistent);
107 }
108
109 EFI_STATUS efivar_set_int(CHAR16 *name, UINTN i, BOOLEAN persistent) {
110 CHAR16 str[32];
111
112 SPrint(str, 32, L"%d", i);
113 return efivar_set(name, str, persistent);
114 }
115
116 EFI_STATUS efivar_get(CHAR16 *name, CHAR16 **value) {
117 CHAR8 *buf;
118 CHAR16 *val;
119 UINTN size;
120 EFI_STATUS err;
121
122 err = efivar_get_raw(&loader_guid, name, &buf, &size);
123 if (EFI_ERROR(err))
124 return err;
125
126 val = StrDuplicate((CHAR16 *)buf);
127 if (!val) {
128 FreePool(buf);
129 return EFI_OUT_OF_RESOURCES;
130 }
131
132 *value = val;
133 return EFI_SUCCESS;
134 }
135
136 EFI_STATUS efivar_get_int(CHAR16 *name, UINTN *i) {
137 CHAR16 *val;
138 EFI_STATUS err;
139
140 err = efivar_get(name, &val);
141 if (!EFI_ERROR(err)) {
142 *i = Atoi(val);
143 FreePool(val);
144 }
145 return err;
146 }
147
148 EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size) {
149 CHAR8 *buf;
150 UINTN l;
151 EFI_STATUS err;
152
153 l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
154 buf = AllocatePool(l);
155 if (!buf)
156 return EFI_OUT_OF_RESOURCES;
157
158 err = uefi_call_wrapper(RT->GetVariable, 5, name, (EFI_GUID *)vendor, NULL, &l, buf);
159 if (!EFI_ERROR(err)) {
160 *buffer = buf;
161 if (size)
162 *size = l;
163 } else
164 FreePool(buf);
165 return err;
166
167 }
168
169 VOID efivar_set_time_usec(CHAR16 *name, UINT64 usec) {
170 CHAR16 str[32];
171
172 if (usec == 0)
173 usec = time_usec();
174 if (usec == 0)
175 return;
176
177 SPrint(str, 32, L"%ld", usec);
178 efivar_set(name, str, FALSE);
179 }
180
181 static INTN utf8_to_16(CHAR8 *stra, CHAR16 *c) {
182 CHAR16 unichar;
183 UINTN len;
184 UINTN i;
185
186 if (stra[0] < 0x80)
187 len = 1;
188 else if ((stra[0] & 0xe0) == 0xc0)
189 len = 2;
190 else if ((stra[0] & 0xf0) == 0xe0)
191 len = 3;
192 else if ((stra[0] & 0xf8) == 0xf0)
193 len = 4;
194 else if ((stra[0] & 0xfc) == 0xf8)
195 len = 5;
196 else if ((stra[0] & 0xfe) == 0xfc)
197 len = 6;
198 else
199 return -1;
200
201 switch (len) {
202 case 1:
203 unichar = stra[0];
204 break;
205 case 2:
206 unichar = stra[0] & 0x1f;
207 break;
208 case 3:
209 unichar = stra[0] & 0x0f;
210 break;
211 case 4:
212 unichar = stra[0] & 0x07;
213 break;
214 case 5:
215 unichar = stra[0] & 0x03;
216 break;
217 case 6:
218 unichar = stra[0] & 0x01;
219 break;
220 }
221
222 for (i = 1; i < len; i++) {
223 if ((stra[i] & 0xc0) != 0x80)
224 return -1;
225 unichar <<= 6;
226 unichar |= stra[i] & 0x3f;
227 }
228
229 *c = unichar;
230 return len;
231 }
232
233 CHAR16 *stra_to_str(CHAR8 *stra) {
234 UINTN strlen;
235 UINTN len;
236 UINTN i;
237 CHAR16 *str;
238
239 len = strlena(stra);
240 str = AllocatePool((len + 1) * sizeof(CHAR16));
241
242 strlen = 0;
243 i = 0;
244 while (i < len) {
245 INTN utf8len;
246
247 utf8len = utf8_to_16(stra + i, str + strlen);
248 if (utf8len <= 0) {
249 /* invalid utf8 sequence, skip the garbage */
250 i++;
251 continue;
252 }
253
254 strlen++;
255 i += utf8len;
256 }
257 str[strlen] = '\0';
258 return str;
259 }
260
261 CHAR16 *stra_to_path(CHAR8 *stra) {
262 CHAR16 *str;
263 UINTN strlen;
264 UINTN len;
265 UINTN i;
266
267 len = strlena(stra);
268 str = AllocatePool((len + 2) * sizeof(CHAR16));
269
270 str[0] = '\\';
271 strlen = 1;
272 i = 0;
273 while (i < len) {
274 INTN utf8len;
275
276 utf8len = utf8_to_16(stra + i, str + strlen);
277 if (utf8len <= 0) {
278 /* invalid utf8 sequence, skip the garbage */
279 i++;
280 continue;
281 }
282
283 if (str[strlen] == '/')
284 str[strlen] = '\\';
285 if (str[strlen] == '\\' && str[strlen-1] == '\\') {
286 /* skip double slashes */
287 i += utf8len;
288 continue;
289 }
290
291 strlen++;
292 i += utf8len;
293 }
294 str[strlen] = '\0';
295 return str;
296 }
297
298 CHAR8 *strchra(CHAR8 *s, CHAR8 c) {
299 do {
300 if (*s == c)
301 return s;
302 } while (*s++);
303 return NULL;
304 }
305
306 INTN file_read(EFI_FILE_HANDLE dir, CHAR16 *name, UINTN off, UINTN size, CHAR8 **content) {
307 EFI_FILE_HANDLE handle;
308 CHAR8 *buf;
309 UINTN buflen;
310 EFI_STATUS err;
311 UINTN len;
312
313 err = uefi_call_wrapper(dir->Open, 5, dir, &handle, name, EFI_FILE_MODE_READ, 0ULL);
314 if (EFI_ERROR(err))
315 return err;
316
317 if (size == 0) {
318 EFI_FILE_INFO *info;
319
320 info = LibFileInfo(handle);
321 buflen = info->FileSize+1;
322 FreePool(info);
323 } else
324 buflen = size;
325
326 if (off > 0) {
327 err = uefi_call_wrapper(handle->SetPosition, 2, handle, off);
328 if (EFI_ERROR(err))
329 return err;
330 }
331
332 buf = AllocatePool(buflen);
333 err = uefi_call_wrapper(handle->Read, 3, handle, &buflen, buf);
334 if (!EFI_ERROR(err)) {
335 buf[buflen] = '\0';
336 *content = buf;
337 len = buflen;
338 } else {
339 len = err;
340 FreePool(buf);
341 }
342
343 uefi_call_wrapper(handle->Close, 1, handle);
344 return len;
345 }