mirror of
https://github.com/u-boot/u-boot.git
synced 2025-04-11 07:24:46 +00:00
efi_loader: add an EFI variable with the file contents
Previous patches enabled SetVariableRT using a RAM backend. Although EBBR [0] defines a variable format we can teach userspace tools and write the altered variables, it's better if we skip the ABI requirements completely. So let's add a new variable, in its own namespace called "VarToFile" which contains a binary dump of the updated RT, BS and, NV variables and will be updated when GetVariable is called. Some adjustments are needed to do that. Currently we discard BS-only variables in EBS(). We need to preserve those on the RAM backend that exposes the variables. Since BS-only variables can't appear at runtime we need to move the memory masking checks from efi_var_collect() to efi_get_next_variable_name_mem()/ efi_get_variable_mem() and do the filtering at runtime. We also need an efi_var_collect() variant available at runtime, in order to construct the "VarToFile" buffer on the fly. All users and applications (for linux) have to do when updating a variable is dd that variable in the file described by "RTStorageVolatile". Linux efivarfs uses a first 4 bytes of the output to represent attributes in little-endian format. So, storing variables works like this: $~ efibootmgr -n 0001 $~ dd if=/sys/firmware/efi/efivars/VarToFile-b2ac5fc9-92b7-4acd-aeac-11e818c3130c of=/boot/efi/ubootefi.var skip=4 bs=1 [0] https://arm-software.github.io/ebbr/index.html#document-chapter5-variable-storage Suggested-by: Ard Biesheuvel <ardb@kernel.org> # dumping all variables to a variable Co-developed-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com> # contributed on efi_var_collect_mem() Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com> Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
This commit is contained in:
parent
bc3dd2493e
commit
00da8d65a3
7 changed files with 146 additions and 65 deletions
|
@ -271,13 +271,16 @@ const efi_guid_t *efi_auth_var_get_guid(const u16 *name);
|
|||
*
|
||||
* @variable_name_size: size of variable_name buffer in bytes
|
||||
* @variable_name: name of uefi variable's name in u16
|
||||
* @mask: bitmask with required attributes of variables to be collected.
|
||||
* variables are only collected if all of the required
|
||||
* attributes match. Use 0 to skip matching
|
||||
* @vendor: vendor's guid
|
||||
*
|
||||
* Return: status code
|
||||
*/
|
||||
efi_status_t __efi_runtime
|
||||
efi_get_next_variable_name_mem(efi_uintn_t *variable_name_size, u16 *variable_name,
|
||||
efi_guid_t *vendor);
|
||||
efi_guid_t *vendor, u32 mask);
|
||||
/**
|
||||
* efi_get_variable_mem() - Runtime common code across efi variable
|
||||
* implementations for GetVariable() from
|
||||
|
@ -289,12 +292,15 @@ efi_get_next_variable_name_mem(efi_uintn_t *variable_name_size, u16 *variable_na
|
|||
* @data_size: size of the buffer to which the variable value is copied
|
||||
* @data: buffer to which the variable value is copied
|
||||
* @timep: authentication time (seconds since start of epoch)
|
||||
* @mask: bitmask with required attributes of variables to be collected.
|
||||
* variables are only collected if all of the required
|
||||
* attributes match. Use 0 to skip matching
|
||||
* Return: status code
|
||||
*/
|
||||
efi_status_t __efi_runtime
|
||||
efi_get_variable_mem(const u16 *variable_name, const efi_guid_t *vendor,
|
||||
u32 *attributes, efi_uintn_t *data_size, void *data,
|
||||
u64 *timep);
|
||||
u64 *timep, u32 mask);
|
||||
|
||||
/**
|
||||
* efi_get_variable_runtime() - runtime implementation of GetVariable()
|
||||
|
@ -334,4 +340,10 @@ efi_get_next_variable_name_runtime(efi_uintn_t *variable_name_size,
|
|||
*/
|
||||
void efi_var_buf_update(struct efi_var_file *var_buf);
|
||||
|
||||
efi_status_t __efi_runtime efi_var_collect_mem(struct efi_var_file *buf,
|
||||
efi_uintn_t *lenp,
|
||||
u32 check_attr_mask);
|
||||
|
||||
u32 efi_var_entry_len(struct efi_var_entry *var);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -387,7 +387,7 @@ int u16_strcasecmp(const u16 *s1, const u16 *s2)
|
|||
* > 0 if the first different u16 in s1 is greater than the
|
||||
* corresponding u16 in s2
|
||||
*/
|
||||
int u16_strncmp(const u16 *s1, const u16 *s2, size_t n)
|
||||
int __efi_runtime u16_strncmp(const u16 *s1, const u16 *s2, size_t n)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
|
|
|
@ -130,6 +130,8 @@ efi_status_t efi_init_runtime_supported(void)
|
|||
EFI_RT_SUPPORTED_CONVERT_POINTER;
|
||||
|
||||
if (IS_ENABLED(CONFIG_EFI_RT_VOLATILE_STORE)) {
|
||||
u8 s = 0;
|
||||
|
||||
ret = efi_set_variable_int(u"RTStorageVolatile",
|
||||
&efi_guid_efi_rt_var_file,
|
||||
EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
||||
|
@ -141,6 +143,29 @@ efi_status_t efi_init_runtime_supported(void)
|
|||
log_err("Failed to set RTStorageVolatile\n");
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* This variable needs to be visible so users can read it,
|
||||
* but the real contents are going to be filled during
|
||||
* GetVariable
|
||||
*/
|
||||
ret = efi_set_variable_int(u"VarToFile",
|
||||
&efi_guid_efi_rt_var_file,
|
||||
EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
||||
EFI_VARIABLE_RUNTIME_ACCESS |
|
||||
EFI_VARIABLE_READ_ONLY,
|
||||
sizeof(s),
|
||||
&s, false);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
log_err("Failed to set VarToFile\n");
|
||||
efi_set_variable_int(u"RTStorageVolatile",
|
||||
&efi_guid_efi_rt_var_file,
|
||||
EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
||||
EFI_VARIABLE_RUNTIME_ACCESS |
|
||||
EFI_VARIABLE_READ_ONLY,
|
||||
0, NULL, false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
rt_table->runtime_services_supported |= EFI_RT_SUPPORTED_SET_VARIABLE;
|
||||
}
|
||||
|
||||
|
|
|
@ -182,7 +182,8 @@ efi_get_variable_runtime(u16 *variable_name, const efi_guid_t *guid,
|
|||
{
|
||||
efi_status_t ret;
|
||||
|
||||
ret = efi_get_variable_mem(variable_name, guid, attributes, data_size, data, NULL);
|
||||
ret = efi_get_variable_mem(variable_name, guid, attributes, data_size,
|
||||
data, NULL, EFI_VARIABLE_RUNTIME_ACCESS);
|
||||
|
||||
/* Remove EFI_VARIABLE_READ_ONLY flag */
|
||||
if (attributes)
|
||||
|
@ -195,7 +196,8 @@ efi_status_t __efi_runtime EFIAPI
|
|||
efi_get_next_variable_name_runtime(efi_uintn_t *variable_name_size,
|
||||
u16 *variable_name, efi_guid_t *guid)
|
||||
{
|
||||
return efi_get_next_variable_name_mem(variable_name_size, variable_name, guid);
|
||||
return efi_get_next_variable_name_mem(variable_name_size, variable_name,
|
||||
guid, EFI_VARIABLE_RUNTIME_ACCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -61,6 +61,23 @@ efi_var_mem_compare(struct efi_var_entry *var, const efi_guid_t *guid,
|
|||
return match;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_var_entry_len() - Get the entry len including headers & name
|
||||
*
|
||||
* @var: pointer to variable start
|
||||
*
|
||||
* Return: 8-byte aligned variable entry length
|
||||
*/
|
||||
|
||||
u32 __efi_runtime efi_var_entry_len(struct efi_var_entry *var)
|
||||
{
|
||||
if (!var)
|
||||
return 0;
|
||||
|
||||
return ALIGN((sizeof(u16) * (u16_strlen(var->name) + 1)) +
|
||||
var->length + sizeof(*var), 8);
|
||||
}
|
||||
|
||||
struct efi_var_entry __efi_runtime
|
||||
*efi_var_mem_find(const efi_guid_t *guid, const u16 *name,
|
||||
struct efi_var_entry **next)
|
||||
|
@ -184,53 +201,6 @@ u64 __efi_runtime efi_var_mem_free(void)
|
|||
sizeof(struct efi_var_entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_var_mem_bs_del() - delete boot service only variables
|
||||
*/
|
||||
static void efi_var_mem_bs_del(void)
|
||||
{
|
||||
struct efi_var_entry *var = efi_var_buf->var;
|
||||
|
||||
for (;;) {
|
||||
struct efi_var_entry *last;
|
||||
|
||||
last = (struct efi_var_entry *)
|
||||
((uintptr_t)efi_var_buf + efi_var_buf->length);
|
||||
if (var >= last)
|
||||
break;
|
||||
if (var->attr & EFI_VARIABLE_RUNTIME_ACCESS) {
|
||||
u16 *data;
|
||||
|
||||
/* skip variable */
|
||||
for (data = var->name; *data; ++data)
|
||||
;
|
||||
++data;
|
||||
var = (struct efi_var_entry *)
|
||||
ALIGN((uintptr_t)data + var->length, 8);
|
||||
} else {
|
||||
/* delete variable */
|
||||
efi_var_mem_del(var);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_var_mem_notify_exit_boot_services() - ExitBootService callback
|
||||
*
|
||||
* @event: callback event
|
||||
* @context: callback context
|
||||
*/
|
||||
static void EFIAPI
|
||||
efi_var_mem_notify_exit_boot_services(struct efi_event *event, void *context)
|
||||
{
|
||||
EFI_ENTRY("%p, %p", event, context);
|
||||
|
||||
/* Delete boot service only variables */
|
||||
efi_var_mem_bs_del();
|
||||
|
||||
EFI_EXIT(EFI_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_var_mem_notify_exit_boot_services() - SetVirtualMemoryMap callback
|
||||
*
|
||||
|
@ -261,11 +231,7 @@ efi_status_t efi_var_mem_init(void)
|
|||
efi_var_buf->magic = EFI_VAR_FILE_MAGIC;
|
||||
efi_var_buf->length = (uintptr_t)efi_var_buf->var -
|
||||
(uintptr_t)efi_var_buf;
|
||||
/* crc32 for 0 bytes = 0 */
|
||||
|
||||
ret = efi_create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
|
||||
efi_var_mem_notify_exit_boot_services, NULL,
|
||||
NULL, &event);
|
||||
if (ret != EFI_SUCCESS)
|
||||
return ret;
|
||||
ret = efi_create_event(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE, TPL_CALLBACK,
|
||||
|
@ -276,10 +242,71 @@ efi_status_t efi_var_mem_init(void)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_var_collect_mem() - Copy EFI variables matching attributes mask from
|
||||
* efi_var_buf
|
||||
*
|
||||
* @buf: buffer containing variable collection
|
||||
* @lenp: buffer length
|
||||
* @mask: mask of matched attributes
|
||||
*
|
||||
* Return: Status code
|
||||
*/
|
||||
efi_status_t __efi_runtime
|
||||
efi_var_collect_mem(struct efi_var_file *buf, efi_uintn_t *lenp, u32 mask)
|
||||
{
|
||||
static struct efi_var_file __efi_runtime_data hdr = {
|
||||
.magic = EFI_VAR_FILE_MAGIC,
|
||||
};
|
||||
struct efi_var_entry *last, *var, *var_to;
|
||||
|
||||
hdr.length = sizeof(struct efi_var_file);
|
||||
|
||||
var = efi_var_buf->var;
|
||||
last = (struct efi_var_entry *)
|
||||
((uintptr_t)efi_var_buf + efi_var_buf->length);
|
||||
if (buf)
|
||||
var_to = buf->var;
|
||||
|
||||
while (var < last) {
|
||||
u32 len = efi_var_entry_len(var);
|
||||
|
||||
if ((var->attr & mask) != mask) {
|
||||
var = (void *)((uintptr_t)var + len);
|
||||
continue;
|
||||
}
|
||||
|
||||
hdr.length += len;
|
||||
|
||||
if (buf && hdr.length <= *lenp) {
|
||||
efi_memcpy_runtime(var_to, var, len);
|
||||
var_to = (void *)var_to + len;
|
||||
}
|
||||
var = (void *)var + len;
|
||||
}
|
||||
|
||||
if (!buf && hdr.length <= *lenp) {
|
||||
*lenp = hdr.length;
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!buf || hdr.length > *lenp) {
|
||||
*lenp = hdr.length;
|
||||
return EFI_BUFFER_TOO_SMALL;
|
||||
}
|
||||
hdr.crc32 = crc32(0, (u8 *)buf->var,
|
||||
hdr.length - sizeof(struct efi_var_file));
|
||||
|
||||
efi_memcpy_runtime(buf, &hdr, sizeof(hdr));
|
||||
*lenp = hdr.length;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
efi_status_t __efi_runtime
|
||||
efi_get_variable_mem(const u16 *variable_name, const efi_guid_t *vendor,
|
||||
u32 *attributes, efi_uintn_t *data_size, void *data,
|
||||
u64 *timep)
|
||||
u64 *timep, u32 mask)
|
||||
{
|
||||
efi_uintn_t old_size;
|
||||
struct efi_var_entry *var;
|
||||
|
@ -291,11 +318,22 @@ efi_get_variable_mem(const u16 *variable_name, const efi_guid_t *vendor,
|
|||
if (!var)
|
||||
return EFI_NOT_FOUND;
|
||||
|
||||
/*
|
||||
* This function is used at runtime to dump EFI variables.
|
||||
* The memory backend we keep around has BS-only variables as
|
||||
* well. At runtime we filter them here
|
||||
*/
|
||||
if (mask && !((var->attr & mask) == mask))
|
||||
return EFI_NOT_FOUND;
|
||||
|
||||
if (attributes)
|
||||
*attributes = var->attr;
|
||||
if (timep)
|
||||
*timep = var->time;
|
||||
|
||||
if (!u16_strcmp(variable_name, u"VarToFile"))
|
||||
return efi_var_collect_mem(data, data_size, EFI_VARIABLE_NON_VOLATILE);
|
||||
|
||||
old_size = *data_size;
|
||||
*data_size = var->length;
|
||||
if (old_size < var->length)
|
||||
|
@ -315,7 +353,8 @@ efi_get_variable_mem(const u16 *variable_name, const efi_guid_t *vendor,
|
|||
|
||||
efi_status_t __efi_runtime
|
||||
efi_get_next_variable_name_mem(efi_uintn_t *variable_name_size,
|
||||
u16 *variable_name, efi_guid_t *vendor)
|
||||
u16 *variable_name, efi_guid_t *vendor,
|
||||
u32 mask)
|
||||
{
|
||||
struct efi_var_entry *var;
|
||||
efi_uintn_t len, old_size;
|
||||
|
@ -324,6 +363,7 @@ efi_get_next_variable_name_mem(efi_uintn_t *variable_name_size,
|
|||
if (!variable_name_size || !variable_name || !vendor)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
skip:
|
||||
len = *variable_name_size >> 1;
|
||||
if (u16_strnlen(variable_name, len) == len)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
@ -347,6 +387,11 @@ efi_get_next_variable_name_mem(efi_uintn_t *variable_name_size,
|
|||
efi_memcpy_runtime(variable_name, var->name, *variable_name_size);
|
||||
efi_memcpy_runtime(vendor, &var->guid, sizeof(efi_guid_t));
|
||||
|
||||
if (mask && !((var->attr & mask) == mask)) {
|
||||
*variable_name_size = old_size;
|
||||
goto skip;
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
@ -209,14 +209,16 @@ efi_get_variable_int(const u16 *variable_name, const efi_guid_t *vendor,
|
|||
u32 *attributes, efi_uintn_t *data_size, void *data,
|
||||
u64 *timep)
|
||||
{
|
||||
return efi_get_variable_mem(variable_name, vendor, attributes, data_size, data, timep);
|
||||
return efi_get_variable_mem(variable_name, vendor, attributes, data_size,
|
||||
data, timep, 0);
|
||||
}
|
||||
|
||||
efi_status_t __efi_runtime
|
||||
efi_get_next_variable_name_int(efi_uintn_t *variable_name_size,
|
||||
u16 *variable_name, efi_guid_t *vendor)
|
||||
{
|
||||
return efi_get_next_variable_name_mem(variable_name_size, variable_name, vendor);
|
||||
return efi_get_next_variable_name_mem(variable_name_size, variable_name,
|
||||
vendor, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -959,11 +959,6 @@ void efi_variables_boot_exit_notify(void)
|
|||
log_err("Unable to notify the MM partition for ExitBootServices\n");
|
||||
free(comm_buf);
|
||||
|
||||
/*
|
||||
* Populate the list for runtime variables.
|
||||
* asking EFI_VARIABLE_RUNTIME_ACCESS is redundant, since
|
||||
* efi_var_mem_notify_exit_boot_services will clean those, but that's fine
|
||||
*/
|
||||
ret = efi_var_collect(&var_buf, &len, EFI_VARIABLE_RUNTIME_ACCESS);
|
||||
if (ret != EFI_SUCCESS)
|
||||
log_err("Can't populate EFI variables. No runtime variables will be available\n");
|
||||
|
|
Loading…
Add table
Reference in a new issue