2017-11-18 17:09:20 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
2015-02-08 12:25:35 +01:00
|
|
|
/*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2.1 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012-2013 Kay Sievers <kay@vrfy.org>
|
|
|
|
* Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#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.
|
|
|
|
*/
|
|
|
|
static 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;
|
|
|
|
}
|
2015-04-11 10:23:23 +02:00
|
|
|
#elif defined(__i386__)
|
2015-02-08 12:25:35 +01:00
|
|
|
UINT64 ticks_read(VOID) {
|
|
|
|
UINT64 val;
|
|
|
|
__asm__ volatile ("rdtsc" : "=A" (val));
|
|
|
|
return val;
|
|
|
|
}
|
2015-04-11 10:23:25 +02:00
|
|
|
#else
|
|
|
|
UINT64 ticks_read(VOID) {
|
|
|
|
UINT64 val = 1;
|
|
|
|
return val;
|
|
|
|
}
|
2015-02-08 12:25:35 +01:00
|
|
|
#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;
|
|
|
|
}
|
|
|
|
|
2015-03-11 23:26:48 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-02-08 12:25:35 +01:00
|
|
|
EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 *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, (CHAR8 *)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) {
|
|
|
|
CHAR8 *buf;
|
|
|
|
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) {
|
|
|
|
FreePool(buf);
|
|
|
|
return EFI_OUT_OF_RESOURCES;
|
|
|
|
}
|
|
|
|
|
|
|
|
*value = val;
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
EFI_STATUS efivar_get_int(CHAR16 *name, UINTN *i) {
|
|
|
|
CHAR16 *val;
|
|
|
|
EFI_STATUS err;
|
|
|
|
|
|
|
|
err = efivar_get(name, &val);
|
|
|
|
if (!EFI_ERROR(err)) {
|
|
|
|
*i = Atoi(val);
|
|
|
|
FreePool(val);
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size) {
|
|
|
|
CHAR8 *buf;
|
|
|
|
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;
|
|
|
|
if (size)
|
|
|
|
*size = l;
|
|
|
|
} else
|
|
|
|
FreePool(buf);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
INTN file_read(EFI_FILE_HANDLE dir, CHAR16 *name, UINTN off, UINTN size, CHAR8 **content) {
|
|
|
|
EFI_FILE_HANDLE handle;
|
|
|
|
CHAR8 *buf;
|
|
|
|
UINTN buflen;
|
|
|
|
EFI_STATUS err;
|
|
|
|
UINTN len;
|
|
|
|
|
|
|
|
err = uefi_call_wrapper(dir->Open, 5, dir, &handle, name, EFI_FILE_MODE_READ, 0ULL);
|
|
|
|
if (EFI_ERROR(err))
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (size == 0) {
|
|
|
|
EFI_FILE_INFO *info;
|
|
|
|
|
|
|
|
info = LibFileInfo(handle);
|
|
|
|
buflen = info->FileSize+1;
|
|
|
|
FreePool(info);
|
|
|
|
} else
|
|
|
|
buflen = size;
|
|
|
|
|
|
|
|
if (off > 0) {
|
|
|
|
err = uefi_call_wrapper(handle->SetPosition, 2, handle, off);
|
|
|
|
if (EFI_ERROR(err))
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf = AllocatePool(buflen);
|
|
|
|
err = uefi_call_wrapper(handle->Read, 3, handle, &buflen, buf);
|
|
|
|
if (!EFI_ERROR(err)) {
|
|
|
|
buf[buflen] = '\0';
|
|
|
|
*content = buf;
|
|
|
|
len = buflen;
|
|
|
|
} else {
|
|
|
|
len = err;
|
|
|
|
FreePool(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
uefi_call_wrapper(handle->Close, 1, handle);
|
|
|
|
return len;
|
|
|
|
}
|