Systemd/src/boot/efi/util.c
Lennart Poettering 0e2bc73274 sd-boot: write the IDs of all discovered entries to an EFI variable
This is primarily useful for debugging, but can be useful for other
purposes too. For example userspace could check whether "auto-windows"
is included in the list, before triggering a boot-into-windows
operation.
2018-10-16 16:44:34 +02:00

325 lines
8.4 KiB
C

/* SPDX-License-Identifier: LGPL-2.1+ */
#include <efi.h>
#include <efilib.h>
#include "util.h"
/*
* Allocated random UUID, intended to be shared across tools that implement
* the (ESP)\loader\entries\<vendor>-<revision>.conf convention and the
* associated EFI variables.
*/
const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} };
#ifdef __x86_64__
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 val;
__asm__ volatile ("rdtsc" : "=A" (val));
return val;
}
#else
UINT64 ticks_read(VOID) {
UINT64 val = 1;
return val;
}
#endif
/* count TSC ticks during a millisecond delay */
UINT64 ticks_freq(VOID) {
UINT64 ticks_start, ticks_end;
ticks_start = ticks_read();
uefi_call_wrapper(BS->Stall, 1, 1000);
ticks_end = ticks_read();
return (ticks_end - ticks_start) * 1000;
}
UINT64 time_usec(VOID) {
UINT64 ticks;
static UINT64 freq;
ticks = ticks_read();
if (ticks == 0)
return 0;
if (freq == 0) {
freq = ticks_freq();
if (freq == 0)
return 0;
}
return 1000 * 1000 * ticks / freq;
}
EFI_STATUS parse_boolean(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) {
*b = TRUE;
return EFI_SUCCESS;
}
if (strcmpa(v, (CHAR8 *)"0") == 0 ||
strcmpa(v, (CHAR8 *)"no") == 0 ||
strcmpa(v, (CHAR8 *)"n") == 0 ||
strcmpa(v, (CHAR8 *)"false") == 0) {
*b = FALSE;
return EFI_SUCCESS;
}
return EFI_INVALID_PARAMETER;
}
EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, VOID *buf, UINTN size, BOOLEAN persistent) {
UINT32 flags;
flags = EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
if (persistent)
flags |= EFI_VARIABLE_NON_VOLATILE;
return uefi_call_wrapper(RT->SetVariable, 5, name, (EFI_GUID *)vendor, flags, size, buf);
}
EFI_STATUS efivar_set(CHAR16 *name, CHAR16 *value, BOOLEAN persistent) {
return efivar_set_raw(&loader_guid, name, value, value ? (StrLen(value)+1) * sizeof(CHAR16) : 0, persistent);
}
EFI_STATUS efivar_set_int(CHAR16 *name, UINTN i, BOOLEAN persistent) {
CHAR16 str[32];
SPrint(str, 32, L"%d", i);
return efivar_set(name, str, persistent);
}
EFI_STATUS efivar_get(CHAR16 *name, CHAR16 **value) {
_cleanup_freepool_ CHAR8 *buf = NULL;
CHAR16 *val;
UINTN size;
EFI_STATUS err;
err = efivar_get_raw(&loader_guid, name, &buf, &size);
if (EFI_ERROR(err))
return err;
val = StrDuplicate((CHAR16 *)buf);
if (!val)
return EFI_OUT_OF_RESOURCES;
*value = val;
return EFI_SUCCESS;
}
EFI_STATUS efivar_get_int(CHAR16 *name, UINTN *i) {
_cleanup_freepool_ CHAR16 *val = NULL;
EFI_STATUS err;
err = efivar_get(name, &val);
if (!EFI_ERROR(err))
*i = Atoi(val);
return err;
}
EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size) {
_cleanup_freepool_ CHAR8 *buf = NULL;
UINTN l;
EFI_STATUS err;
l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
buf = AllocatePool(l);
if (!buf)
return EFI_OUT_OF_RESOURCES;
err = uefi_call_wrapper(RT->GetVariable, 5, name, (EFI_GUID *)vendor, NULL, &l, buf);
if (!EFI_ERROR(err)) {
*buffer = buf;
buf = NULL;
if (size)
*size = l;
}
return err;
}
VOID efivar_set_time_usec(CHAR16 *name, UINT64 usec) {
CHAR16 str[32];
if (usec == 0)
usec = time_usec();
if (usec == 0)
return;
SPrint(str, 32, L"%ld", usec);
efivar_set(name, str, FALSE);
}
static INTN utf8_to_16(CHAR8 *stra, CHAR16 *c) {
CHAR16 unichar;
UINTN len;
UINTN i;
if (stra[0] < 0x80)
len = 1;
else if ((stra[0] & 0xe0) == 0xc0)
len = 2;
else if ((stra[0] & 0xf0) == 0xe0)
len = 3;
else if ((stra[0] & 0xf8) == 0xf0)
len = 4;
else if ((stra[0] & 0xfc) == 0xf8)
len = 5;
else if ((stra[0] & 0xfe) == 0xfc)
len = 6;
else
return -1;
switch (len) {
case 1:
unichar = stra[0];
break;
case 2:
unichar = stra[0] & 0x1f;
break;
case 3:
unichar = stra[0] & 0x0f;
break;
case 4:
unichar = stra[0] & 0x07;
break;
case 5:
unichar = stra[0] & 0x03;
break;
case 6:
unichar = stra[0] & 0x01;
break;
}
for (i = 1; i < len; i++) {
if ((stra[i] & 0xc0) != 0x80)
return -1;
unichar <<= 6;
unichar |= stra[i] & 0x3f;
}
*c = unichar;
return len;
}
CHAR16 *stra_to_str(CHAR8 *stra) {
UINTN strlen;
UINTN len;
UINTN i;
CHAR16 *str;
len = strlena(stra);
str = AllocatePool((len + 1) * sizeof(CHAR16));
strlen = 0;
i = 0;
while (i < len) {
INTN utf8len;
utf8len = utf8_to_16(stra + i, str + strlen);
if (utf8len <= 0) {
/* invalid utf8 sequence, skip the garbage */
i++;
continue;
}
strlen++;
i += utf8len;
}
str[strlen] = '\0';
return str;
}
CHAR16 *stra_to_path(CHAR8 *stra) {
CHAR16 *str;
UINTN strlen;
UINTN len;
UINTN i;
len = strlena(stra);
str = AllocatePool((len + 2) * sizeof(CHAR16));
str[0] = '\\';
strlen = 1;
i = 0;
while (i < len) {
INTN utf8len;
utf8len = utf8_to_16(stra + i, str + strlen);
if (utf8len <= 0) {
/* invalid utf8 sequence, skip the garbage */
i++;
continue;
}
if (str[strlen] == '/')
str[strlen] = '\\';
if (str[strlen] == '\\' && str[strlen-1] == '\\') {
/* skip double slashes */
i += utf8len;
continue;
}
strlen++;
i += utf8len;
}
str[strlen] = '\0';
return str;
}
CHAR8 *strchra(CHAR8 *s, CHAR8 c) {
do {
if (*s == c)
return s;
} while (*s++);
return NULL;
}
EFI_STATUS file_read(EFI_FILE_HANDLE dir, CHAR16 *name, UINTN off, UINTN size, CHAR8 **content, UINTN *content_size) {
EFI_FILE_HANDLE handle;
_cleanup_freepool_ CHAR8 *buf = NULL;
EFI_STATUS err;
err = uefi_call_wrapper(dir->Open, 5, dir, &handle, name, EFI_FILE_MODE_READ, 0ULL);
if (EFI_ERROR(err))
return err;
if (size == 0) {
_cleanup_freepool_ EFI_FILE_INFO *info;
info = LibFileInfo(handle);
size = info->FileSize+1;
}
if (off > 0) {
err = uefi_call_wrapper(handle->SetPosition, 2, handle, off);
if (EFI_ERROR(err))
return err;
}
buf = AllocatePool(size + 1);
err = uefi_call_wrapper(handle->Read, 3, handle, &size, buf);
if (!EFI_ERROR(err)) {
buf[size] = '\0';
*content = buf;
buf = NULL;
if (content_size)
*content_size = size;
}
uefi_call_wrapper(handle->Close, 1, handle);
return err;
}