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