]>
Commit | Line | Data |
---|---|---|
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 | 13 | const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} }; |
0fa2cac4 KS |
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 | } | |
8403daa2 | 21 | #elif defined(__i386__) |
0fa2cac4 KS |
22 | UINT64 ticks_read(VOID) { |
23 | UINT64 val; | |
24 | __asm__ volatile ("rdtsc" : "=A" (val)); | |
25 | return val; | |
26 | } | |
0d8d3689 KK |
27 | #else |
28 | UINT64 ticks_read(VOID) { | |
29 | UINT64 val = 1; | |
30 | return val; | |
31 | } | |
0fa2cac4 KS |
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 | ||
c027b67f | 42 | return (ticks_end - ticks_start) * 1000UL; |
0fa2cac4 KS |
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 | ||
c027b67f | 59 | return 1000UL * 1000UL * ticks / freq; |
0fa2cac4 KS |
60 | } |
61 | ||
427ee7ec | 62 | EFI_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 | 85 | EFI_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 | 95 | EFI_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 | ||
99 | EFI_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 | 106 | EFI_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 | 141 | EFI_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 | 152 | EFI_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 | ||
175 | VOID 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 | ||
187 | static 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 | ||
239 | CHAR16 *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 | ||
267 | CHAR16 *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 | ||
304 | CHAR8 *strchra(CHAR8 *s, CHAR8 c) { | |
305 | do { | |
306 | if (*s == c) | |
307 | return s; | |
308 | } while (*s++); | |
309 | return NULL; | |
310 | } | |
311 | ||
1aaabb17 LP |
312 | EFI_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 | |
354 | EFI_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 | } |