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