1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <efi.h>
4 #include <efilib.h>
5
6 #include "ticks.h"
7 #include "util.h"
8
parse_boolean(const CHAR8 * v,BOOLEAN * b)9 EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b) {
10 assert(b);
11
12 if (!v)
13 return EFI_INVALID_PARAMETER;
14
15 if (strcmpa(v, (CHAR8 *)"1") == 0 ||
16 strcmpa(v, (CHAR8 *)"yes") == 0 ||
17 strcmpa(v, (CHAR8 *)"y") == 0 ||
18 strcmpa(v, (CHAR8 *)"true") == 0 ||
19 strcmpa(v, (CHAR8 *)"t") == 0 ||
20 strcmpa(v, (CHAR8 *)"on") == 0) {
21 *b = TRUE;
22 return EFI_SUCCESS;
23 }
24
25 if (strcmpa(v, (CHAR8 *)"0") == 0 ||
26 strcmpa(v, (CHAR8 *)"no") == 0 ||
27 strcmpa(v, (CHAR8 *)"n") == 0 ||
28 strcmpa(v, (CHAR8 *)"false") == 0 ||
29 strcmpa(v, (CHAR8 *)"f") == 0 ||
30 strcmpa(v, (CHAR8 *)"off") == 0) {
31 *b = FALSE;
32 return EFI_SUCCESS;
33 }
34
35 return EFI_INVALID_PARAMETER;
36 }
37
efivar_set_raw(const EFI_GUID * vendor,const CHAR16 * name,const void * buf,UINTN size,UINT32 flags)38 EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, const CHAR16 *name, const void *buf, UINTN size, UINT32 flags) {
39 assert(vendor);
40 assert(name);
41 assert(buf || size == 0);
42
43 flags |= EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;
44 return RT->SetVariable((CHAR16 *) name, (EFI_GUID *) vendor, flags, size, (void *) buf);
45 }
46
efivar_set(const EFI_GUID * vendor,const CHAR16 * name,const CHAR16 * value,UINT32 flags)47 EFI_STATUS efivar_set(const EFI_GUID *vendor, const CHAR16 *name, const CHAR16 *value, UINT32 flags) {
48 assert(vendor);
49 assert(name);
50
51 return efivar_set_raw(vendor, name, value, value ? StrSize(value) : 0, flags);
52 }
53
efivar_set_uint_string(const EFI_GUID * vendor,const CHAR16 * name,UINTN i,UINT32 flags)54 EFI_STATUS efivar_set_uint_string(const EFI_GUID *vendor, const CHAR16 *name, UINTN i, UINT32 flags) {
55 CHAR16 str[32];
56
57 assert(vendor);
58 assert(name);
59
60 /* Note that SPrint has no native sized length specifier and will always use ValueToString()
61 * regardless of what sign we tell it to use. Therefore, UINTN_MAX will come out as -1 on
62 * 64bit machines. */
63 ValueToString(str, FALSE, i);
64 return efivar_set(vendor, name, str, flags);
65 }
66
efivar_set_uint32_le(const EFI_GUID * vendor,const CHAR16 * name,UINT32 value,UINT32 flags)67 EFI_STATUS efivar_set_uint32_le(const EFI_GUID *vendor, const CHAR16 *name, UINT32 value, UINT32 flags) {
68 UINT8 buf[4];
69
70 assert(vendor);
71 assert(name);
72
73 buf[0] = (UINT8)(value >> 0U & 0xFF);
74 buf[1] = (UINT8)(value >> 8U & 0xFF);
75 buf[2] = (UINT8)(value >> 16U & 0xFF);
76 buf[3] = (UINT8)(value >> 24U & 0xFF);
77
78 return efivar_set_raw(vendor, name, buf, sizeof(buf), flags);
79 }
80
efivar_set_uint64_le(const EFI_GUID * vendor,const CHAR16 * name,UINT64 value,UINT32 flags)81 EFI_STATUS efivar_set_uint64_le(const EFI_GUID *vendor, const CHAR16 *name, UINT64 value, UINT32 flags) {
82 UINT8 buf[8];
83
84 assert(vendor);
85 assert(name);
86
87 buf[0] = (UINT8)(value >> 0U & 0xFF);
88 buf[1] = (UINT8)(value >> 8U & 0xFF);
89 buf[2] = (UINT8)(value >> 16U & 0xFF);
90 buf[3] = (UINT8)(value >> 24U & 0xFF);
91 buf[4] = (UINT8)(value >> 32U & 0xFF);
92 buf[5] = (UINT8)(value >> 40U & 0xFF);
93 buf[6] = (UINT8)(value >> 48U & 0xFF);
94 buf[7] = (UINT8)(value >> 56U & 0xFF);
95
96 return efivar_set_raw(vendor, name, buf, sizeof(buf), flags);
97 }
98
efivar_get(const EFI_GUID * vendor,const CHAR16 * name,CHAR16 ** value)99 EFI_STATUS efivar_get(const EFI_GUID *vendor, const CHAR16 *name, CHAR16 **value) {
100 _cleanup_freepool_ CHAR16 *buf = NULL;
101 EFI_STATUS err;
102 CHAR16 *val;
103 UINTN size;
104
105 assert(vendor);
106 assert(name);
107
108 err = efivar_get_raw(vendor, name, (CHAR8**)&buf, &size);
109 if (EFI_ERROR(err))
110 return err;
111
112 /* Make sure there are no incomplete characters in the buffer */
113 if ((size % sizeof(CHAR16)) != 0)
114 return EFI_INVALID_PARAMETER;
115
116 if (!value)
117 return EFI_SUCCESS;
118
119 /* Return buffer directly if it happens to be NUL terminated already */
120 if (size >= sizeof(CHAR16) && buf[size / sizeof(CHAR16) - 1] == 0) {
121 *value = TAKE_PTR(buf);
122 return EFI_SUCCESS;
123 }
124
125 /* Make sure a terminating NUL is available at the end */
126 val = xallocate_pool(size + sizeof(CHAR16));
127
128 CopyMem(val, buf, size);
129 val[size / sizeof(CHAR16) - 1] = 0; /* NUL terminate */
130
131 *value = val;
132 return EFI_SUCCESS;
133 }
134
efivar_get_uint_string(const EFI_GUID * vendor,const CHAR16 * name,UINTN * i)135 EFI_STATUS efivar_get_uint_string(const EFI_GUID *vendor, const CHAR16 *name, UINTN *i) {
136 _cleanup_freepool_ CHAR16 *val = NULL;
137 EFI_STATUS err;
138
139 assert(vendor);
140 assert(name);
141 assert(i);
142
143 err = efivar_get(vendor, name, &val);
144 if (!EFI_ERROR(err))
145 *i = Atoi(val);
146
147 return err;
148 }
149
efivar_get_uint32_le(const EFI_GUID * vendor,const CHAR16 * name,UINT32 * ret)150 EFI_STATUS efivar_get_uint32_le(const EFI_GUID *vendor, const CHAR16 *name, UINT32 *ret) {
151 _cleanup_freepool_ CHAR8 *buf = NULL;
152 UINTN size;
153 EFI_STATUS err;
154
155 assert(vendor);
156 assert(name);
157
158 err = efivar_get_raw(vendor, name, &buf, &size);
159 if (!EFI_ERROR(err) && ret) {
160 if (size != sizeof(UINT32))
161 return EFI_BUFFER_TOO_SMALL;
162
163 *ret = (UINT32) buf[0] << 0U | (UINT32) buf[1] << 8U | (UINT32) buf[2] << 16U |
164 (UINT32) buf[3] << 24U;
165 }
166
167 return err;
168 }
169
efivar_get_uint64_le(const EFI_GUID * vendor,const CHAR16 * name,UINT64 * ret)170 EFI_STATUS efivar_get_uint64_le(const EFI_GUID *vendor, const CHAR16 *name, UINT64 *ret) {
171 _cleanup_freepool_ CHAR8 *buf = NULL;
172 UINTN size;
173 EFI_STATUS err;
174
175 assert(vendor);
176 assert(name);
177
178 err = efivar_get_raw(vendor, name, &buf, &size);
179 if (!EFI_ERROR(err) && ret) {
180 if (size != sizeof(UINT64))
181 return EFI_BUFFER_TOO_SMALL;
182
183 *ret = (UINT64) buf[0] << 0U | (UINT64) buf[1] << 8U | (UINT64) buf[2] << 16U |
184 (UINT64) buf[3] << 24U | (UINT64) buf[4] << 32U | (UINT64) buf[5] << 40U |
185 (UINT64) buf[6] << 48U | (UINT64) buf[7] << 56U;
186 }
187
188 return err;
189 }
190
efivar_get_raw(const EFI_GUID * vendor,const CHAR16 * name,CHAR8 ** buffer,UINTN * size)191 EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const CHAR16 *name, CHAR8 **buffer, UINTN *size) {
192 _cleanup_freepool_ CHAR8 *buf = NULL;
193 UINTN l;
194 EFI_STATUS err;
195
196 assert(vendor);
197 assert(name);
198
199 l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
200 buf = xallocate_pool(l);
201
202 err = RT->GetVariable((CHAR16 *) name, (EFI_GUID *) vendor, NULL, &l, buf);
203 if (!EFI_ERROR(err)) {
204
205 if (buffer)
206 *buffer = TAKE_PTR(buf);
207
208 if (size)
209 *size = l;
210 }
211
212 return err;
213 }
214
efivar_get_boolean_u8(const EFI_GUID * vendor,const CHAR16 * name,BOOLEAN * ret)215 EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const CHAR16 *name, BOOLEAN *ret) {
216 _cleanup_freepool_ CHAR8 *b = NULL;
217 UINTN size;
218 EFI_STATUS err;
219
220 assert(vendor);
221 assert(name);
222 assert(ret);
223
224 err = efivar_get_raw(vendor, name, &b, &size);
225 if (!EFI_ERROR(err))
226 *ret = *b > 0;
227
228 return err;
229 }
230
efivar_set_time_usec(const EFI_GUID * vendor,const CHAR16 * name,UINT64 usec)231 void efivar_set_time_usec(const EFI_GUID *vendor, const CHAR16 *name, UINT64 usec) {
232 CHAR16 str[32];
233
234 assert(vendor);
235 assert(name);
236
237 if (usec == 0)
238 usec = time_usec();
239 if (usec == 0)
240 return;
241
242 /* See comment on ValueToString in efivar_set_uint_string(). */
243 ValueToString(str, FALSE, usec);
244 efivar_set(vendor, name, str, 0);
245 }
246
utf8_to_16(const CHAR8 * stra,CHAR16 * c)247 static INTN utf8_to_16(const CHAR8 *stra, CHAR16 *c) {
248 CHAR16 unichar;
249 UINTN len;
250
251 assert(stra);
252 assert(c);
253
254 if (!(stra[0] & 0x80))
255 len = 1;
256 else if ((stra[0] & 0xe0) == 0xc0)
257 len = 2;
258 else if ((stra[0] & 0xf0) == 0xe0)
259 len = 3;
260 else if ((stra[0] & 0xf8) == 0xf0)
261 len = 4;
262 else if ((stra[0] & 0xfc) == 0xf8)
263 len = 5;
264 else if ((stra[0] & 0xfe) == 0xfc)
265 len = 6;
266 else
267 return -1;
268
269 switch (len) {
270 case 1:
271 unichar = stra[0];
272 break;
273 case 2:
274 unichar = stra[0] & 0x1f;
275 break;
276 case 3:
277 unichar = stra[0] & 0x0f;
278 break;
279 case 4:
280 unichar = stra[0] & 0x07;
281 break;
282 case 5:
283 unichar = stra[0] & 0x03;
284 break;
285 case 6:
286 unichar = stra[0] & 0x01;
287 break;
288 }
289
290 for (UINTN i = 1; i < len; i++) {
291 if ((stra[i] & 0xc0) != 0x80)
292 return -1;
293 unichar <<= 6;
294 unichar |= stra[i] & 0x3f;
295 }
296
297 *c = unichar;
298 return len;
299 }
300
xstra_to_str(const CHAR8 * stra)301 CHAR16 *xstra_to_str(const CHAR8 *stra) {
302 UINTN strlen;
303 UINTN len;
304 UINTN i;
305 CHAR16 *str;
306
307 assert(stra);
308
309 len = strlena(stra);
310 str = xnew(CHAR16, len + 1);
311
312 strlen = 0;
313 i = 0;
314 while (i < len) {
315 INTN utf8len;
316
317 utf8len = utf8_to_16(stra + i, str + strlen);
318 if (utf8len <= 0) {
319 /* invalid utf8 sequence, skip the garbage */
320 i++;
321 continue;
322 }
323
324 strlen++;
325 i += utf8len;
326 }
327 str[strlen] = '\0';
328 return str;
329 }
330
xstra_to_path(const CHAR8 * stra)331 CHAR16 *xstra_to_path(const CHAR8 *stra) {
332 CHAR16 *str;
333 UINTN strlen;
334 UINTN len;
335 UINTN i;
336
337 assert(stra);
338
339 len = strlena(stra);
340 str = xnew(CHAR16, len + 2);
341
342 str[0] = '\\';
343 strlen = 1;
344 i = 0;
345 while (i < len) {
346 INTN utf8len;
347
348 utf8len = utf8_to_16(stra + i, str + strlen);
349 if (utf8len <= 0) {
350 /* invalid utf8 sequence, skip the garbage */
351 i++;
352 continue;
353 }
354
355 if (str[strlen] == '/')
356 str[strlen] = '\\';
357 if (str[strlen] == '\\' && str[strlen-1] == '\\') {
358 /* skip double slashes */
359 i += utf8len;
360 continue;
361 }
362
363 strlen++;
364 i += utf8len;
365 }
366 str[strlen] = '\0';
367 return str;
368 }
369
strchra(const CHAR8 * s,CHAR8 c)370 CHAR8 *strchra(const CHAR8 *s, CHAR8 c) {
371 if (!s)
372 return NULL;
373
374 do {
375 if (*s == c)
376 return (CHAR8*) s;
377 } while (*s++);
378
379 return NULL;
380 }
381
file_read(EFI_FILE * dir,const CHAR16 * name,UINTN off,UINTN size,CHAR8 ** ret,UINTN * ret_size)382 EFI_STATUS file_read(EFI_FILE *dir, const CHAR16 *name, UINTN off, UINTN size, CHAR8 **ret, UINTN *ret_size) {
383 _cleanup_(file_closep) EFI_FILE *handle = NULL;
384 _cleanup_freepool_ CHAR8 *buf = NULL;
385 EFI_STATUS err;
386
387 assert(dir);
388 assert(name);
389 assert(ret);
390
391 err = dir->Open(dir, &handle, (CHAR16*) name, EFI_FILE_MODE_READ, 0ULL);
392 if (EFI_ERROR(err))
393 return err;
394
395 if (size == 0) {
396 _cleanup_freepool_ EFI_FILE_INFO *info = NULL;
397
398 err = get_file_info_harder(handle, &info, NULL);
399 if (EFI_ERROR(err))
400 return err;
401
402 size = info->FileSize;
403 }
404
405 if (off > 0) {
406 err = handle->SetPosition(handle, off);
407 if (EFI_ERROR(err))
408 return err;
409 }
410
411 /* Allocate some extra bytes to guarantee the result is NUL-terminated for CHAR8 and CHAR16 strings. */
412 UINTN extra = size % sizeof(CHAR16) + sizeof(CHAR16);
413
414 buf = xallocate_pool(size + extra);
415 err = handle->Read(handle, &size, buf);
416 if (EFI_ERROR(err))
417 return err;
418
419 /* Note that handle->Read() changes size to reflect the actually bytes read. */
420 ZeroMem(buf + size, extra);
421
422 *ret = TAKE_PTR(buf);
423 if (ret_size)
424 *ret_size = size;
425
426 return err;
427 }
428
log_error_stall(const CHAR16 * fmt,...)429 void log_error_stall(const CHAR16 *fmt, ...) {
430 va_list args;
431
432 assert(fmt);
433
434 INT32 attr = ST->ConOut->Mode->Attribute;
435 ST->ConOut->SetAttribute(ST->ConOut, EFI_LIGHTRED|EFI_BACKGROUND_BLACK);
436
437 if (ST->ConOut->Mode->CursorColumn > 0)
438 Print(L"\n");
439
440 va_start(args, fmt);
441 VPrint(fmt, args);
442 va_end(args);
443
444 Print(L"\n");
445
446 ST->ConOut->SetAttribute(ST->ConOut, attr);
447
448 /* Give the user a chance to see the message. */
449 BS->Stall(3 * 1000 * 1000);
450 }
451
log_oom(void)452 EFI_STATUS log_oom(void) {
453 log_error_stall(L"Out of memory.");
454 return EFI_OUT_OF_RESOURCES;
455 }
456
print_at(UINTN x,UINTN y,UINTN attr,const CHAR16 * str)457 void print_at(UINTN x, UINTN y, UINTN attr, const CHAR16 *str) {
458 assert(str);
459 ST->ConOut->SetCursorPosition(ST->ConOut, x, y);
460 ST->ConOut->SetAttribute(ST->ConOut, attr);
461 ST->ConOut->OutputString(ST->ConOut, (CHAR16*)str);
462 }
463
clear_screen(UINTN attr)464 void clear_screen(UINTN attr) {
465 ST->ConOut->SetAttribute(ST->ConOut, attr);
466 ST->ConOut->ClearScreen(ST->ConOut);
467 }
468
sort_pointer_array(void ** array,UINTN n_members,compare_pointer_func_t compare)469 void sort_pointer_array(
470 void **array,
471 UINTN n_members,
472 compare_pointer_func_t compare) {
473
474 assert(array || n_members == 0);
475 assert(compare);
476
477 if (n_members <= 1)
478 return;
479
480 for (UINTN i = 1; i < n_members; i++) {
481 UINTN k;
482 void *entry = array[i];
483
484 for (k = i; k > 0; k--) {
485 if (compare(array[k - 1], entry) <= 0)
486 break;
487
488 array[k] = array[k - 1];
489 }
490
491 array[k] = entry;
492 }
493 }
494
get_file_info_harder(EFI_FILE * handle,EFI_FILE_INFO ** ret,UINTN * ret_size)495 EFI_STATUS get_file_info_harder(
496 EFI_FILE *handle,
497 EFI_FILE_INFO **ret,
498 UINTN *ret_size) {
499
500 UINTN size = offsetof(EFI_FILE_INFO, FileName) + 256;
501 _cleanup_freepool_ EFI_FILE_INFO *fi = NULL;
502 EFI_STATUS err;
503
504 assert(handle);
505 assert(ret);
506
507 /* A lot like LibFileInfo() but with useful error propagation */
508
509 fi = xallocate_pool(size);
510 err = handle->GetInfo(handle, &GenericFileInfo, &size, fi);
511 if (err == EFI_BUFFER_TOO_SMALL) {
512 FreePool(fi);
513 fi = xallocate_pool(size); /* GetInfo tells us the required size, let's use that now */
514 err = handle->GetInfo(handle, &GenericFileInfo, &size, fi);
515 }
516
517 if (EFI_ERROR(err))
518 return err;
519
520 *ret = TAKE_PTR(fi);
521
522 if (ret_size)
523 *ret_size = size;
524
525 return EFI_SUCCESS;
526 }
527
readdir_harder(EFI_FILE * handle,EFI_FILE_INFO ** buffer,UINTN * buffer_size)528 EFI_STATUS readdir_harder(
529 EFI_FILE *handle,
530 EFI_FILE_INFO **buffer,
531 UINTN *buffer_size) {
532
533 EFI_STATUS err;
534 UINTN sz;
535
536 assert(handle);
537 assert(buffer);
538 assert(buffer_size);
539
540 /* buffer/buffer_size are both in and output parameters. Should be zero-initialized initially, and
541 * the specified buffer needs to be freed by caller, after final use. */
542
543 if (!*buffer) {
544 /* Some broken firmware violates the EFI spec by still advancing the readdir
545 * position when returning EFI_BUFFER_TOO_SMALL, effectively skipping over any files when
546 * the buffer was too small. Therefore, start with a buffer that should handle FAT32 max
547 * file name length.
548 * As a side effect, most readdir_harder() calls will now be slightly faster. */
549 sz = sizeof(EFI_FILE_INFO) + 256 * sizeof(CHAR16);
550 *buffer = xallocate_pool(sz);
551 *buffer_size = sz;
552 } else
553 sz = *buffer_size;
554
555 err = handle->Read(handle, &sz, *buffer);
556 if (err == EFI_BUFFER_TOO_SMALL) {
557 FreePool(*buffer);
558 *buffer = xallocate_pool(sz);
559 *buffer_size = sz;
560 err = handle->Read(handle, &sz, *buffer);
561 }
562 if (EFI_ERROR(err))
563 return err;
564
565 if (sz == 0) {
566 /* End of directory */
567 FreePool(*buffer);
568 *buffer = NULL;
569 *buffer_size = 0;
570 }
571
572 return EFI_SUCCESS;
573 }
574
strnlena(const CHAR8 * p,UINTN maxlen)575 UINTN strnlena(const CHAR8 *p, UINTN maxlen) {
576 UINTN c;
577
578 if (!p)
579 return 0;
580
581 for (c = 0; c < maxlen; c++)
582 if (p[c] == 0)
583 break;
584
585 return c;
586 }
587
strncasecmpa(const CHAR8 * a,const CHAR8 * b,UINTN maxlen)588 INTN strncasecmpa(const CHAR8 *a, const CHAR8 *b, UINTN maxlen) {
589 if (!a || !b)
590 return CMP(a, b);
591
592 while (maxlen > 0) {
593 CHAR8 ca = *a, cb = *b;
594 if (ca >= 'A' && ca <= 'Z')
595 ca += 'a' - 'A';
596 if (cb >= 'A' && cb <= 'Z')
597 cb += 'a' - 'A';
598 if (!ca || ca != cb)
599 return ca - cb;
600
601 a++;
602 b++;
603 maxlen--;
604 }
605
606 return 0;
607 }
608
xstrndup8(const CHAR8 * p,UINTN sz)609 CHAR8 *xstrndup8(const CHAR8 *p, UINTN sz) {
610 CHAR8 *n;
611
612 /* Following efilib's naming scheme this function would be called strndupa(), but we already have a
613 * function named like this in userspace, and it does something different there, hence to minimize
614 * confusion, let's pick a different name here */
615
616 assert(p || sz == 0);
617
618 sz = strnlena(p, sz);
619
620 n = xallocate_pool(sz + 1);
621
622 if (sz > 0)
623 CopyMem(n, p, sz);
624 n[sz] = 0;
625
626 return n;
627 }
628
is_ascii(const CHAR16 * f)629 BOOLEAN is_ascii(const CHAR16 *f) {
630 if (!f)
631 return FALSE;
632
633 for (; *f != 0; f++)
634 if (*f > 127)
635 return FALSE;
636
637 return TRUE;
638 }
639
strv_free(CHAR16 ** v)640 CHAR16 **strv_free(CHAR16 **v) {
641 if (!v)
642 return NULL;
643
644 for (CHAR16 **i = v; *i; i++)
645 FreePool(*i);
646
647 FreePool(v);
648 return NULL;
649 }
650
open_directory(EFI_FILE * root,const CHAR16 * path,EFI_FILE ** ret)651 EFI_STATUS open_directory(
652 EFI_FILE *root,
653 const CHAR16 *path,
654 EFI_FILE **ret) {
655
656 _cleanup_(file_closep) EFI_FILE *dir = NULL;
657 _cleanup_freepool_ EFI_FILE_INFO *file_info = NULL;
658 EFI_STATUS err;
659
660 assert(root);
661
662 /* Opens a file, and then verifies it is actually a directory */
663
664 err = root->Open(root, &dir, (CHAR16*) path, EFI_FILE_MODE_READ, 0ULL);
665 if (EFI_ERROR(err))
666 return err;
667
668 err = get_file_info_harder(dir, &file_info, NULL);
669 if (EFI_ERROR(err))
670 return err;
671 if (!FLAGS_SET(file_info->Attribute, EFI_FILE_DIRECTORY))
672 return EFI_LOAD_ERROR;
673
674 *ret = TAKE_PTR(dir);
675 return EFI_SUCCESS;
676 }
677
get_os_indications_supported(void)678 UINT64 get_os_indications_supported(void) {
679 UINT64 osind;
680 EFI_STATUS err;
681
682 /* Returns the supported OS indications. If we can't acquire it, returns a zeroed out mask, i.e. no
683 * supported features. */
684
685 err = efivar_get_uint64_le(EFI_GLOBAL_GUID, L"OsIndicationsSupported", &osind);
686 if (EFI_ERROR(err))
687 return 0;
688
689 return osind;
690 }
691
692 #ifdef EFI_DEBUG
debug_break(void)693 __attribute__((noinline)) void debug_break(void) {
694 /* This is a poor programmer's breakpoint to wait until a debugger
695 * has attached to us. Just "set variable wait = 0" or "return" to continue. */
696 volatile BOOLEAN wait = TRUE;
697 while (wait)
698 /* Prefer asm based stalling so that gdb has a source location to present. */
699 #if defined(__i386__) || defined(__x86_64__)
700 asm volatile("pause");
701 #elif defined(__aarch64__)
702 asm volatile("wfi");
703 #else
704 BS->Stall(5000);
705 #endif
706 }
707 #endif
708
709 #if defined(__i386__) || defined(__x86_64__)
inb(UINT16 port)710 static inline UINT8 inb(UINT16 port) {
711 UINT8 value;
712 asm volatile("inb %1, %0" : "=a"(value) : "Nd"(port));
713 return value;
714 }
715
outb(UINT16 port,UINT8 value)716 static inline void outb(UINT16 port, UINT8 value) {
717 asm volatile("outb %0, %1" : : "a"(value), "Nd"(port));
718 }
719
beep(UINTN beep_count)720 void beep(UINTN beep_count) {
721 enum {
722 PITCH = 500,
723 BEEP_DURATION_USEC = 100 * 1000,
724 WAIT_DURATION_USEC = 400 * 1000,
725
726 PIT_FREQUENCY = 0x1234dd,
727 SPEAKER_CONTROL_PORT = 0x61,
728 SPEAKER_ON_MASK = 0x03,
729 TIMER_PORT_MAGIC = 0xB6,
730 TIMER_CONTROL_PORT = 0x43,
731 TIMER_CONTROL2_PORT = 0x42,
732 };
733
734 /* Set frequency. */
735 UINT32 counter = PIT_FREQUENCY / PITCH;
736 outb(TIMER_CONTROL_PORT, TIMER_PORT_MAGIC);
737 outb(TIMER_CONTROL2_PORT, counter & 0xFF);
738 outb(TIMER_CONTROL2_PORT, (counter >> 8) & 0xFF);
739
740 UINT8 value = inb(SPEAKER_CONTROL_PORT);
741
742 while (beep_count > 0) {
743 /* Turn speaker on. */
744 value |= SPEAKER_ON_MASK;
745 outb(SPEAKER_CONTROL_PORT, value);
746
747 BS->Stall(BEEP_DURATION_USEC);
748
749 /* Turn speaker off. */
750 value &= ~SPEAKER_ON_MASK;
751 outb(SPEAKER_CONTROL_PORT, value);
752
753 beep_count--;
754 if (beep_count > 0)
755 BS->Stall(WAIT_DURATION_USEC);
756 }
757 }
758 #endif
759