]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/boot/efi/util.c
Merge pull request #21981 from medhefgo/boot-cleanup
[thirdparty/systemd.git] / src / boot / efi / util.c
index 932a4cfa850b86276c652e0fdbae6a831accf1ff..ab47e108989feb7efcc2f54ebd3d1024295a32f4 100644 (file)
@@ -6,50 +6,50 @@
 #include "util.h"
 
 #ifdef __x86_64__
-UINT64 ticks_read(VOID) {
+UINT64 ticks_read(void) {
         UINT64 a, d;
         __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d));
         return (d << 32) | a;
 }
 #elif defined(__i386__)
-UINT64 ticks_read(VOID) {
+UINT64 ticks_read(void) {
         UINT64 val;
         __asm__ volatile ("rdtsc" : "=A" (val));
         return val;
 }
 #elif defined(__aarch64__)
-UINT64 ticks_read(VOID) {
+UINT64 ticks_read(void) {
         UINT64 val;
         __asm__ volatile ("mrs %0, cntpct_el0" : "=r" (val));
         return val;
 }
 #else
-UINT64 ticks_read(VOID) {
+UINT64 ticks_read(void) {
         UINT64 val = 1;
         return val;
 }
 #endif
 
 #if defined(__aarch64__)
-UINT64 ticks_freq(VOID) {
+UINT64 ticks_freq(void) {
         UINT64 freq;
         __asm__ volatile ("mrs %0, cntfrq_el0": "=r" (freq));
         return freq;
 }
 #else
 /* count TSC ticks during a millisecond delay */
-UINT64 ticks_freq(VOID) {
+UINT64 ticks_freq(void) {
         UINT64 ticks_start, ticks_end;
 
         ticks_start = ticks_read();
-        uefi_call_wrapper(BS->Stall, 1, 1000);
+        BS->Stall(1000);
         ticks_end = ticks_read();
 
         return (ticks_end - ticks_start) * 1000UL;
 }
 #endif
 
-UINT64 time_usec(VOID) {
+UINT64 time_usec(void) {
         UINT64 ticks;
         static UINT64 freq;
 
@@ -75,7 +75,9 @@ EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b) {
         if (strcmpa(v, (CHAR8 *)"1") == 0 ||
             strcmpa(v, (CHAR8 *)"yes") == 0 ||
             strcmpa(v, (CHAR8 *)"y") == 0 ||
-            strcmpa(v, (CHAR8 *)"true") == 0) {
+            strcmpa(v, (CHAR8 *)"true") == 0 ||
+            strcmpa(v, (CHAR8 *)"t") == 0 ||
+            strcmpa(v, (CHAR8 *)"on") == 0) {
                 *b = TRUE;
                 return EFI_SUCCESS;
         }
@@ -83,7 +85,9 @@ EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b) {
         if (strcmpa(v, (CHAR8 *)"0") == 0 ||
             strcmpa(v, (CHAR8 *)"no") == 0 ||
             strcmpa(v, (CHAR8 *)"n") == 0 ||
-            strcmpa(v, (CHAR8 *)"false") == 0) {
+            strcmpa(v, (CHAR8 *)"false") == 0 ||
+            strcmpa(v, (CHAR8 *)"f") == 0 ||
+            strcmpa(v, (CHAR8 *)"off") == 0) {
                 *b = FALSE;
                 return EFI_SUCCESS;
         }
@@ -91,20 +95,20 @@ EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b) {
         return EFI_INVALID_PARAMETER;
 }
 
-EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, const CHAR16 *name, const VOID *buf, UINTN size, UINT32 flags) {
+EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, const CHAR16 *name, const void *buf, UINTN size, UINT32 flags) {
         assert(vendor);
         assert(name);
         assert(buf || size == 0);
 
         flags |= EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;
-        return uefi_call_wrapper(RT->SetVariable, 5, (CHAR16*) name, (EFI_GUID *)vendor, flags, size, (VOID*) buf);
+        return RT->SetVariable((CHAR16 *) name, (EFI_GUID *) vendor, flags, size, (void *) buf);
 }
 
 EFI_STATUS efivar_set(const EFI_GUID *vendor, const CHAR16 *name, const CHAR16 *value, UINT32 flags) {
         assert(vendor);
         assert(name);
 
-        return efivar_set_raw(vendor, name, value, value ? (StrLen(value) + 1) * sizeof(CHAR16) : 0, flags);
+        return efivar_set_raw(vendor, name, value, value ? StrSize(value) : 0, flags);
 }
 
 EFI_STATUS efivar_set_uint_string(const EFI_GUID *vendor, const CHAR16 *name, UINTN i, UINT32 flags) {
@@ -170,18 +174,16 @@ EFI_STATUS efivar_get(const EFI_GUID *vendor, const CHAR16 *name, CHAR16 **value
                 return EFI_SUCCESS;
 
         /* Return buffer directly if it happens to be NUL terminated already */
-        if (size >= sizeof(CHAR16) && buf[size/sizeof(CHAR16)] == 0) {
+        if (size >= sizeof(CHAR16) && buf[size / sizeof(CHAR16) - 1] == 0) {
                 *value = TAKE_PTR(buf);
                 return EFI_SUCCESS;
         }
 
         /* Make sure a terminating NUL is available at the end */
-        val = AllocatePool(size + sizeof(CHAR16));
-        if (!val)
-                return EFI_OUT_OF_RESOURCES;
+        val = xallocate_pool(size + sizeof(CHAR16));
 
         CopyMem(val, buf, size);
-        val[size / sizeof(CHAR16)] = 0; /* NUL terminate */
+        val[size / sizeof(CHAR16) - 1] = 0; /* NUL terminate */
 
         *value = val;
         return EFI_SUCCESS;
@@ -252,11 +254,9 @@ EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const CHAR16 *name, CHAR8 **bu
         assert(name);
 
         l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
-        buf = AllocatePool(l);
-        if (!buf)
-                return EFI_OUT_OF_RESOURCES;
+        buf = xallocate_pool(l);
 
-        err = uefi_call_wrapper(RT->GetVariable, 5, (CHAR16*) name, (EFI_GUID *)vendor, NULL, &l, buf);
+        err = RT->GetVariable((CHAR16 *) name, (EFI_GUID *) vendor, NULL, &l, buf);
         if (!EFI_ERROR(err)) {
 
                 if (buffer)
@@ -285,7 +285,7 @@ EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const CHAR16 *name, BOO
         return err;
 }
 
-VOID efivar_set_time_usec(const EFI_GUID *vendor, const CHAR16 *name, UINT64 usec) {
+void efivar_set_time_usec(const EFI_GUID *vendor, const CHAR16 *name, UINT64 usec) {
         CHAR16 str[32];
 
         assert(vendor);
@@ -354,7 +354,7 @@ static INTN utf8_to_16(const CHAR8 *stra, CHAR16 *c) {
         return len;
 }
 
-CHAR16 *stra_to_str(const CHAR8 *stra) {
+CHAR16 *xstra_to_str(const CHAR8 *stra) {
         UINTN strlen;
         UINTN len;
         UINTN i;
@@ -363,7 +363,7 @@ CHAR16 *stra_to_str(const CHAR8 *stra) {
         assert(stra);
 
         len = strlena(stra);
-        str = AllocatePool((len + 1) * sizeof(CHAR16));
+        str = xnew(CHAR16, len + 1);
 
         strlen = 0;
         i = 0;
@@ -384,7 +384,7 @@ CHAR16 *stra_to_str(const CHAR8 *stra) {
         return str;
 }
 
-CHAR16 *stra_to_path(const CHAR8 *stra) {
+CHAR16 *xstra_to_path(const CHAR8 *stra) {
         CHAR16 *str;
         UINTN strlen;
         UINTN len;
@@ -393,7 +393,7 @@ CHAR16 *stra_to_path(const CHAR8 *stra) {
         assert(stra);
 
         len = strlena(stra);
-        str = AllocatePool((len + 2) * sizeof(CHAR16));
+        str = xnew(CHAR16, len + 2);
 
         str[0] = '\\';
         strlen = 1;
@@ -424,47 +424,47 @@ CHAR16 *stra_to_path(const CHAR8 *stra) {
 }
 
 CHAR8 *strchra(const CHAR8 *s, CHAR8 c) {
-        assert(s);
+        if (!s)
+                return NULL;
+
         do {
                 if (*s == c)
                         return (CHAR8*) s;
         } while (*s++);
+
         return NULL;
 }
 
 EFI_STATUS file_read(EFI_FILE_HANDLE dir, const CHAR16 *name, UINTN off, UINTN size, CHAR8 **ret, UINTN *ret_size) {
-        _cleanup_(FileHandleClosep) EFI_FILE_HANDLE handle = NULL;
+        _cleanup_(file_handle_closep) EFI_FILE_HANDLE handle = NULL;
         _cleanup_freepool_ CHAR8 *buf = NULL;
         EFI_STATUS err;
 
         assert(name);
         assert(ret);
 
-        err = uefi_call_wrapper(dir->Open, 5, dir, &handle, (CHAR16*) name, EFI_FILE_MODE_READ, 0ULL);
+        err = dir->Open(dir, &handle, (CHAR16*) name, EFI_FILE_MODE_READ, 0ULL);
         if (EFI_ERROR(err))
                 return err;
 
         if (size == 0) {
                 _cleanup_freepool_ EFI_FILE_INFO *info = NULL;
 
-                info = LibFileInfo(handle);
-                if (!info)
-                        return EFI_OUT_OF_RESOURCES;
+                err = get_file_info_harder(handle, &info, NULL);
+                if (EFI_ERROR(err))
+                        return err;
 
                 size = info->FileSize+1;
         }
 
         if (off > 0) {
-                err = uefi_call_wrapper(handle->SetPosition, 2, handle, off);
+                err = handle->SetPosition(handle, off);
                 if (EFI_ERROR(err))
                         return err;
         }
 
-        buf = AllocatePool(size + 1);
-        if (!buf)
-                return EFI_OUT_OF_RESOURCES;
-
-        err = uefi_call_wrapper(handle->Read, 3, handle, &size, buf);
+        buf = xallocate_pool(size + 1);
+        err = handle->Read(handle, &size, buf);
         if (EFI_ERROR(err))
                 return err;
 
@@ -477,23 +477,281 @@ EFI_STATUS file_read(EFI_FILE_HANDLE dir, const CHAR16 *name, UINTN off, UINTN s
         return err;
 }
 
-VOID log_error_stall(const CHAR16 *fmt, ...) {
+void log_error_stall(const CHAR16 *fmt, ...) {
         va_list args;
 
         assert(fmt);
 
-        uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTRED|EFI_BACKGROUND_BLACK);
+        INT32 attr = ST->ConOut->Mode->Attribute;
+        ST->ConOut->SetAttribute(ST->ConOut, EFI_LIGHTRED|EFI_BACKGROUND_BLACK);
+
+        if (ST->ConOut->Mode->CursorColumn > 0)
+                Print(L"\n");
 
-        Print(L"\n");
         va_start(args, fmt);
         VPrint(fmt, args);
         va_end(args);
+
         Print(L"\n");
 
-        uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+        ST->ConOut->SetAttribute(ST->ConOut, attr);
+
+        /* Give the user a chance to see the message. */
+        BS->Stall(3 * 1000 * 1000);
 }
 
 EFI_STATUS log_oom(void) {
         log_error_stall(L"Out of memory.");
         return EFI_OUT_OF_RESOURCES;
 }
+
+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