+
+void print_at(UINTN x, UINTN y, UINTN attr, const CHAR16 *str) {
+ assert(str);
+ ST->ConOut->SetCursorPosition(ST->ConOut, x, y);
+ ST->ConOut->SetAttribute(ST->ConOut, attr);
+ ST->ConOut->OutputString(ST->ConOut, (CHAR16*)str);
+}
+
+void clear_screen(UINTN attr) {
+ ST->ConOut->SetAttribute(ST->ConOut, attr);
+ ST->ConOut->ClearScreen(ST->ConOut);
+}
+
+void sort_pointer_array(
+ void **array,
+ UINTN n_members,
+ compare_pointer_func_t compare) {
+
+ assert(array || n_members == 0);
+ assert(compare);
+
+ if (n_members <= 1)
+ return;
+
+ for (UINTN i = 1; i < n_members; i++) {
+ BOOLEAN more = FALSE;
+
+ for (UINTN k = 0; k < n_members - i; k++) {
+ void *entry;
+
+ if (compare(array[k], array[k+1]) <= 0)
+ continue;
+
+ entry = array[k];
+ array[k] = array[k+1];
+ array[k+1] = entry;
+ more = TRUE;
+ }
+ if (!more)
+ break;
+ }
+}
+
+EFI_STATUS get_file_info_harder(
+ EFI_FILE_HANDLE handle,
+ EFI_FILE_INFO **ret,
+ UINTN *ret_size) {
+
+ UINTN size = offsetof(EFI_FILE_INFO, FileName) + 256;
+ _cleanup_freepool_ EFI_FILE_INFO *fi = NULL;
+ EFI_STATUS err;
+
+ assert(handle);
+ assert(ret);
+
+ /* A lot like LibFileInfo() but with useful error propagation */
+
+ fi = xallocate_pool(size);
+ err = handle->GetInfo(handle, &GenericFileInfo, &size, fi);
+ if (err == EFI_BUFFER_TOO_SMALL) {
+ FreePool(fi);
+ fi = xallocate_pool(size); /* GetInfo tells us the required size, let's use that now */
+ err = handle->GetInfo(handle, &GenericFileInfo, &size, fi);
+ }
+
+ if (EFI_ERROR(err))
+ return err;
+
+ *ret = TAKE_PTR(fi);
+
+ if (ret_size)
+ *ret_size = size;
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS readdir_harder(
+ EFI_FILE_HANDLE handle,
+ EFI_FILE_INFO **buffer,
+ UINTN *buffer_size) {
+
+ EFI_STATUS err;
+ UINTN sz;
+
+ assert(handle);
+ assert(buffer);
+ assert(buffer_size);
+
+ /* buffer/buffer_size are both in and output parameters. Should be zero-initialized initially, and
+ * the specified buffer needs to be freed by caller, after final use. */
+
+ if (!*buffer) {
+ sz = offsetof(EFI_FILE_INFO, FileName) /* + 256 */;
+ *buffer = xallocate_pool(sz);
+ *buffer_size = sz;
+ } else
+ sz = *buffer_size;
+
+ err = handle->Read(handle, &sz, *buffer);
+ if (err == EFI_BUFFER_TOO_SMALL) {
+ FreePool(*buffer);
+ *buffer = xallocate_pool(sz);
+ *buffer_size = sz;
+ err = handle->Read(handle, &sz, *buffer);
+ }
+ if (EFI_ERROR(err))
+ return err;
+
+ if (sz == 0) {
+ /* End of directory */
+ FreePool(*buffer);
+ *buffer = NULL;
+ *buffer_size = 0;
+ }
+
+ return EFI_SUCCESS;
+}
+
+UINTN strnlena(const CHAR8 *p, UINTN maxlen) {
+ UINTN c;
+
+ if (!p)
+ return 0;
+
+ for (c = 0; c < maxlen; c++)
+ if (p[c] == 0)
+ break;
+
+ return c;
+}
+
+INTN strncasecmpa(const CHAR8 *a, const CHAR8 *b, UINTN maxlen) {
+ if (!a || !b)
+ return CMP(a, b);
+
+ while (maxlen > 0) {
+ CHAR8 ca = *a, cb = *b;
+ if (ca >= 'A' && ca <= 'Z')
+ ca += 'a' - 'A';
+ if (cb >= 'A' && cb <= 'Z')
+ cb += 'a' - 'A';
+ if (!ca || ca != cb)
+ return ca - cb;
+
+ a++;
+ b++;
+ maxlen--;
+ }
+
+ return 0;
+}
+
+CHAR8 *xstrndup8(const CHAR8 *p, UINTN sz) {
+ CHAR8 *n;
+
+ /* Following efilib's naming scheme this function would be called strndupa(), but we already have a
+ * function named like this in userspace, and it does something different there, hence to minimize
+ * confusion, let's pick a different name here */
+
+ assert(p || sz == 0);
+
+ sz = strnlena(p, sz);
+
+ n = xallocate_pool(sz + 1);
+
+ if (sz > 0)
+ CopyMem(n, p, sz);
+ n[sz] = 0;
+
+ return n;
+}
+
+BOOLEAN is_ascii(const CHAR16 *f) {
+ if (!f)
+ return FALSE;
+
+ for (; *f != 0; f++)
+ if (*f > 127)
+ return FALSE;
+
+ return TRUE;
+}
+
+CHAR16 **strv_free(CHAR16 **v) {
+ if (!v)
+ return NULL;
+
+ for (CHAR16 **i = v; *i; i++)
+ FreePool(*i);
+
+ FreePool(v);
+ return NULL;
+}
+
+EFI_STATUS open_directory(
+ EFI_FILE_HANDLE root,
+ const CHAR16 *path,
+ EFI_FILE_HANDLE *ret) {
+
+ _cleanup_(file_handle_closep) EFI_FILE_HANDLE dir = NULL;
+ _cleanup_freepool_ EFI_FILE_INFO *file_info = NULL;
+ EFI_STATUS err;
+
+ assert(root);
+
+ /* Opens a file, and then verifies it is actually a directory */
+
+ err = root->Open(root, &dir, (CHAR16*) path, EFI_FILE_MODE_READ, 0ULL);
+ if (EFI_ERROR(err))
+ return err;
+
+ err = get_file_info_harder(dir, &file_info, NULL);
+ if (EFI_ERROR(err))
+ return err;
+ if (!FLAGS_SET(file_info->Attribute, EFI_FILE_DIRECTORY))
+ return EFI_LOAD_ERROR;
+
+ *ret = TAKE_PTR(dir);
+ return EFI_SUCCESS;
+}
+
+UINT64 get_os_indications_supported(void) {
+ UINT64 osind;
+ EFI_STATUS err;
+
+ /* Returns the supported OS indications. If we can't acquire it, returns a zeroed out mask, i.e. no
+ * supported features. */
+
+ err = efivar_get_uint64_le(EFI_GLOBAL_GUID, L"OsIndicationsSupported", &osind);
+ if (EFI_ERROR(err))
+ return 0;
+
+ return osind;
+}
+
+#ifdef EFI_DEBUG
+__attribute__((noinline)) void debug_break(void) {
+ /* This is a poor programmer's breakpoint to wait until a debugger
+ * has attached to us. Just "set variable wait = 0" or "return" to continue. */
+ volatile BOOLEAN wait = TRUE;
+ while (wait)
+ /* Prefer asm based stalling so that gdb has a source location to present. */
+#if defined(__i386__) || defined(__x86_64__)
+ asm volatile("pause");
+#elif defined(__aarch64__)
+ asm volatile("wfi");
+#else
+ BS->Stall(5000);
+#endif
+}
+#endif