lib: uefi: add volatile UEFI variable support

GBL needs to read an UEFI variable called "gbl_debug".
We add 2 commands. "uefi_set_var" to set an UEFI variable. And
"uefi_list_var" to list the UEFI variables.

For GBL, we can use "uefi_set_var gbl_debug 1" to enable its
debug mode.

Signed-off-by: Ying-Chun Liu (PaulLiu) <paulliu@debian.org>
This commit is contained in:
Ying-Chun Liu (PaulLiu)
2025-08-11 04:39:14 +01:00
committed by Kelvin Zhang
parent de3e831eae
commit 45d7da5640
4 changed files with 248 additions and 0 deletions

View File

@@ -22,6 +22,7 @@ MODULE_SRCS += \
$(LOCAL_DIR)/io_stack.cpp \
$(LOCAL_DIR)/debug_support.cpp \
$(LOCAL_DIR)/charset.cpp \
$(LOCAL_DIR)/variable_mem.cpp \
include make/module.mk

View File

@@ -33,6 +33,7 @@
#include <uefi/system_table.h>
#include "boot_service_provider.h"
#include "charset.h"
#include "configuration_table.h"
#include "defer.h"
#include "memory_protocols.h"
@@ -43,6 +44,7 @@
#include "text_protocol.h"
#include "uefi_platform.h"
#include "debug_support.h"
#include "variable_mem.h"
namespace {
@@ -170,8 +172,31 @@ int cmd_uefi_load(int argc, const console_cmd_args *argv) {
return 0;
}
int cmd_uefi_set_variable(int argc, const console_cmd_args *argv) {
if (argc != 3) {
printf("Usage: %s <variable> <data>\n", argv[0].str);
return 1;
}
EfiGuid guid = EFI_GLOBAL_VARIABLE_GUID;
char16_t buffer[128];
utf8_to_utf16(buffer, argv[1].str, sizeof(buffer) / sizeof(buffer[0]));
efi_set_variable(buffer,
&guid,
EFI_VARIABLE_BOOTSERVICE_ACCESS,
argv[2].str,
strlen(argv[2].str));
return 0;
}
int cmd_uefi_list_variable(int argc, const console_cmd_args *argv) {
efi_list_variable();
return 0;
}
STATIC_COMMAND_START
STATIC_COMMAND("uefi_load", "load UEFI application and run it", &cmd_uefi_load)
STATIC_COMMAND("uefi_set_var", "set UEFI variable", &cmd_uefi_set_variable)
STATIC_COMMAND("uefi_list_var", "list UEFI variable", &cmd_uefi_list_variable)
STATIC_COMMAND_END(uefi);
} // namespace

176
lib/uefi/variable_mem.cpp Normal file
View File

@@ -0,0 +1,176 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include "variable_mem.h"
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <lk/list.h>
#include <uefi/types.h>
#include "charset.h"
namespace {
constexpr size_t kVarNameMax = 128;
struct EfiVariable {
struct list_node node;
char16_t VariableName[kVarNameMax];
EfiGuid VendorGuid;
uint32_t Attributes;
char* Data;
size_t DataLen;
};
}
static list_node variables_in_mem = LIST_INITIAL_VALUE(variables_in_mem);
static EfiVariable * search_existing_variable(const char16_t *varname,
const EfiGuid *guid) {
/* search for existing variable */
struct EfiVariable *var = nullptr;
list_for_every_entry(&variables_in_mem, var, struct EfiVariable, node) {
if (guid != NULL && memcmp(&var->VendorGuid, guid, sizeof(EfiGuid)) != 0) {
continue;
}
if (utf16_strcmp(varname, var->VariableName) != 0) {
continue;
}
return var;
}
return nullptr;
}
EfiStatus efi_get_variable(const char16_t *variable_name,
const EfiGuid *guid,
uint32_t *attribute,
char **data,
size_t *data_size) {
struct EfiVariable *var = search_existing_variable(variable_name, guid);
if (data)
*data = nullptr;
if (data_size)
*data_size = 0;
if (attribute)
*attribute = 0;
if (var) {
if (data)
*data = var->Data;
if (data_size)
*data_size = var->DataLen;
if (attribute)
*attribute = var->Attributes;
return SUCCESS;
}
return NOT_FOUND;
}
void efi_set_variable(const char16_t *variable_name,
const EfiGuid *guid,
uint32_t attribute,
const char *data,
size_t data_len) {
struct EfiVariable *var = search_existing_variable(variable_name, guid);
/* alloc new variable if it is not existed */
if (!var) {
var = reinterpret_cast<struct EfiVariable *>(malloc(sizeof(struct EfiVariable)));
memset(var, 0, sizeof(struct EfiVariable));
var->node = LIST_INITIAL_CLEARED_VALUE;
size_t name_len = utf16_strlen(variable_name);
if (name_len >= kVarNameMax)
name_len = kVarNameMax - 1;
memcpy(var->VariableName, variable_name, name_len * sizeof(char16_t));
list_add_tail(&variables_in_mem, &var->node);
}
if (var->Data) {
free(var->Data);
}
var->Data = nullptr;
var->DataLen = 0;
if (data_len > 0) {
var->Data = reinterpret_cast<char *>(malloc(data_len));
memcpy(var->Data, data, data_len);
var->DataLen = data_len;
}
var->Attributes = attribute;
if (guid) {
memcpy(&var->VendorGuid, guid, sizeof(EfiGuid));
}
}
void efi_list_variable(void) {
struct EfiVariable *var = nullptr;
list_for_every_entry(&variables_in_mem, var, struct EfiVariable, node) {
char varname[kVarNameMax];
utf16_to_utf8(varname, var->VariableName, sizeof(varname));
printf("%s\n", varname);
/* print GUID */
printf(" %08x-%04x-%04x",
var->VendorGuid.data1,
var->VendorGuid.data2,
var->VendorGuid.data3);
for (int i = 0; i < 8 ; i++) {
if (i == 0 || i == 2) {
printf("-");
}
printf("%02x", var->VendorGuid.data4[i]);
}
printf("\n");
printf(" ");
/* print attributes */
for (uint32_t attr = 1; attr <= var->Attributes; attr <<= 1) {
if (var->Attributes & attr) {
if (attr == EFI_VARIABLE_NON_VOLATILE) {
printf("NV");
} else if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS) {
printf("BS");
} else if (attr == EFI_VARIABLE_RUNTIME_ACCESS) {
printf("RT");
} else {
printf("0x%02x", attr);
}
if (attr << 1 <= var->Attributes) {
printf("|");
}
}
}
printf(", DataSize = 0x%zx\n", var->DataLen);
/* dump data */
for (size_t i = 0; i < var->DataLen ; i+=16) {
printf(" %08zx:", i);
for (size_t j = 0; j < 16; j++) {
if (i + j < var->DataLen) {
printf(" %02x", var->Data[i + j]);
} else {
printf(" ");
}
}
printf(" ");
for (size_t j = 0; j < 16 && i + j < var->DataLen; j++) {
if (var->Data[i + j] >= 0x20 && var->Data[i + j] < 0x7e) {
printf("%c", var->Data[i + j]);
} else {
printf(".");
}
}
printf("\n");
}
}
}

46
lib/uefi/variable_mem.h Normal file
View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef __VARIABLE_MEM_
#define __VARIABLE_MEM_
#include <uefi/types.h>
static constexpr auto EFI_GLOBAL_VARIABLE_GUID =
EfiGuid{0x8be4df61,
0x93ca,
0x11d2,
{0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c}};
static constexpr uint32_t EFI_VARIABLE_NON_VOLATILE = 0x01;
static constexpr uint32_t EFI_VARIABLE_BOOTSERVICE_ACCESS = 0x02;
static constexpr uint32_t EFI_VARIABLE_RUNTIME_ACCESS = 0x04;
static constexpr uint32_t EFI_VARIABLE_HARDWARE_ERROR_RECORD = 0x08;
EfiStatus efi_get_variable(const char16_t *variable_name,
const EfiGuid *guid,
uint32_t *attribute,
char **data,
size_t *data_size);
void efi_set_variable(const char16_t *variable_name,
const EfiGuid *guid,
uint32_t attribute,
const char *data,
size_t data_len);
void efi_list_variable(void);
#endif