]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/boot/efi/util.c
Merge pull request #10806 from poettering/logind-many-fixes
[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) * 1000UL;
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 1000UL * 1000UL * ticks / freq;
60 }
61
62 EFI_STATUS parse_boolean(CHAR8 *v, BOOLEAN *b) {
63 if (strcmpa(v, (CHAR8 *)"1") == 0 ||
64 strcmpa(v, (CHAR8 *)"yes") == 0 ||
65 strcmpa(v, (CHAR8 *)"y") == 0 ||
66 strcmpa(v, (CHAR8 *)"true") == 0) {
67 *b = TRUE;
68 return EFI_SUCCESS;
69 }
70
71 if (strcmpa(v, (CHAR8 *)"0") == 0 ||
72 strcmpa(v, (CHAR8 *)"no") == 0 ||
73 strcmpa(v, (CHAR8 *)"n") == 0 ||
74 strcmpa(v, (CHAR8 *)"false") == 0) {
75 *b = FALSE;
76 return EFI_SUCCESS;
77 }
78
79 return EFI_INVALID_PARAMETER;
80 }
81
82 EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, VOID *buf, UINTN size, BOOLEAN persistent) {
83 UINT32 flags;
84
85 flags = EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
86 if (persistent)
87 flags |= EFI_VARIABLE_NON_VOLATILE;
88
89 return uefi_call_wrapper(RT->SetVariable, 5, name, (EFI_GUID *)vendor, flags, size, buf);
90 }
91
92 EFI_STATUS efivar_set(CHAR16 *name, CHAR16 *value, BOOLEAN persistent) {
93 return efivar_set_raw(&loader_guid, name, value, value ? (StrLen(value)+1) * sizeof(CHAR16) : 0, persistent);
94 }
95
96 EFI_STATUS efivar_set_int(CHAR16 *name, UINTN i, BOOLEAN persistent) {
97 CHAR16 str[32];
98
99 SPrint(str, 32, L"%d", i);
100 return efivar_set(name, str, persistent);
101 }
102
103 EFI_STATUS efivar_get(CHAR16 *name, CHAR16 **value) {
104 _cleanup_freepool_ CHAR8 *buf = NULL;
105 CHAR16 *val;
106 UINTN size;
107 EFI_STATUS err;
108
109 err = efivar_get_raw(&loader_guid, name, &buf, &size);
110 if (EFI_ERROR(err))
111 return err;
112
113 val = StrDuplicate((CHAR16 *)buf);
114 if (!val)
115 return EFI_OUT_OF_RESOURCES;
116
117 *value = val;
118 return EFI_SUCCESS;
119 }
120
121 EFI_STATUS efivar_get_int(CHAR16 *name, UINTN *i) {
122 _cleanup_freepool_ CHAR16 *val = NULL;
123 EFI_STATUS err;
124
125 err = efivar_get(name, &val);
126 if (!EFI_ERROR(err))
127 *i = Atoi(val);
128
129 return err;
130 }
131
132 EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size) {
133 _cleanup_freepool_ CHAR8 *buf = NULL;
134 UINTN l;
135 EFI_STATUS err;
136
137 l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
138 buf = AllocatePool(l);
139 if (!buf)
140 return EFI_OUT_OF_RESOURCES;
141
142 err = uefi_call_wrapper(RT->GetVariable, 5, name, (EFI_GUID *)vendor, NULL, &l, buf);
143 if (!EFI_ERROR(err)) {
144 *buffer = buf;
145 buf = NULL;
146 if (size)
147 *size = l;
148 }
149
150 return err;
151 }
152
153 VOID efivar_set_time_usec(CHAR16 *name, UINT64 usec) {
154 CHAR16 str[32];
155
156 if (usec == 0)
157 usec = time_usec();
158 if (usec == 0)
159 return;
160
161 SPrint(str, 32, L"%ld", usec);
162 efivar_set(name, str, FALSE);
163 }
164
165 static INTN utf8_to_16(CHAR8 *stra, CHAR16 *c) {
166 CHAR16 unichar;
167 UINTN len;
168 UINTN i;
169
170 if (stra[0] < 0x80)
171 len = 1;
172 else if ((stra[0] & 0xe0) == 0xc0)
173 len = 2;
174 else if ((stra[0] & 0xf0) == 0xe0)
175 len = 3;
176 else if ((stra[0] & 0xf8) == 0xf0)
177 len = 4;
178 else if ((stra[0] & 0xfc) == 0xf8)
179 len = 5;
180 else if ((stra[0] & 0xfe) == 0xfc)
181 len = 6;
182 else
183 return -1;
184
185 switch (len) {
186 case 1:
187 unichar = stra[0];
188 break;
189 case 2:
190 unichar = stra[0] & 0x1f;
191 break;
192 case 3:
193 unichar = stra[0] & 0x0f;
194 break;
195 case 4:
196 unichar = stra[0] & 0x07;
197 break;
198 case 5:
199 unichar = stra[0] & 0x03;
200 break;
201 case 6:
202 unichar = stra[0] & 0x01;
203 break;
204 }
205
206 for (i = 1; i < len; i++) {
207 if ((stra[i] & 0xc0) != 0x80)
208 return -1;
209 unichar <<= 6;
210 unichar |= stra[i] & 0x3f;
211 }
212
213 *c = unichar;
214 return len;
215 }
216
217 CHAR16 *stra_to_str(CHAR8 *stra) {
218 UINTN strlen;
219 UINTN len;
220 UINTN i;
221 CHAR16 *str;
222
223 len = strlena(stra);
224 str = AllocatePool((len + 1) * sizeof(CHAR16));
225
226 strlen = 0;
227 i = 0;
228 while (i < len) {
229 INTN utf8len;
230
231 utf8len = utf8_to_16(stra + i, str + strlen);
232 if (utf8len <= 0) {
233 /* invalid utf8 sequence, skip the garbage */
234 i++;
235 continue;
236 }
237
238 strlen++;
239 i += utf8len;
240 }
241 str[strlen] = '\0';
242 return str;
243 }
244
245 CHAR16 *stra_to_path(CHAR8 *stra) {
246 CHAR16 *str;
247 UINTN strlen;
248 UINTN len;
249 UINTN i;
250
251 len = strlena(stra);
252 str = AllocatePool((len + 2) * sizeof(CHAR16));
253
254 str[0] = '\\';
255 strlen = 1;
256 i = 0;
257 while (i < len) {
258 INTN utf8len;
259
260 utf8len = utf8_to_16(stra + i, str + strlen);
261 if (utf8len <= 0) {
262 /* invalid utf8 sequence, skip the garbage */
263 i++;
264 continue;
265 }
266
267 if (str[strlen] == '/')
268 str[strlen] = '\\';
269 if (str[strlen] == '\\' && str[strlen-1] == '\\') {
270 /* skip double slashes */
271 i += utf8len;
272 continue;
273 }
274
275 strlen++;
276 i += utf8len;
277 }
278 str[strlen] = '\0';
279 return str;
280 }
281
282 CHAR8 *strchra(CHAR8 *s, CHAR8 c) {
283 do {
284 if (*s == c)
285 return s;
286 } while (*s++);
287 return NULL;
288 }
289
290 EFI_STATUS file_read(EFI_FILE_HANDLE dir, CHAR16 *name, UINTN off, UINTN size, CHAR8 **content, UINTN *content_size) {
291 EFI_FILE_HANDLE handle;
292 _cleanup_freepool_ CHAR8 *buf = NULL;
293 EFI_STATUS err;
294
295 err = uefi_call_wrapper(dir->Open, 5, dir, &handle, name, EFI_FILE_MODE_READ, 0ULL);
296 if (EFI_ERROR(err))
297 return err;
298
299 if (size == 0) {
300 _cleanup_freepool_ EFI_FILE_INFO *info;
301
302 info = LibFileInfo(handle);
303 size = info->FileSize+1;
304 }
305
306 if (off > 0) {
307 err = uefi_call_wrapper(handle->SetPosition, 2, handle, off);
308 if (EFI_ERROR(err))
309 return err;
310 }
311
312 buf = AllocatePool(size + 1);
313 err = uefi_call_wrapper(handle->Read, 3, handle, &size, buf);
314 if (!EFI_ERROR(err)) {
315 buf[size] = '\0';
316 *content = buf;
317 buf = NULL;
318 if (content_size)
319 *content_size = size;
320 }
321 uefi_call_wrapper(handle->Close, 1, handle);
322
323 return err;
324 }