| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * efi_selftest_variables |
| * |
| * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de> |
| * |
| * This unit test checks the runtime services for variables: |
| * GetVariable, GetNextVariableName, SetVariable, QueryVariableInfo. |
| */ |
| |
| #include <efi_selftest.h> |
| |
| #define EFI_ST_MAX_DATA_SIZE 16 |
| #define EFI_ST_MAX_VARNAME_SIZE 80 |
| |
| static struct efi_boot_services *boottime; |
| static struct efi_runtime_services *runtime; |
| static const efi_guid_t guid_vendor0 = |
| EFI_GUID(0x67029eb5, 0x0af2, 0xf6b1, |
| 0xda, 0x53, 0xfc, 0xb5, 0x66, 0xdd, 0x1c, 0xe6); |
| static const efi_guid_t guid_vendor1 = |
| EFI_GUID(0xff629290, 0x1fc1, 0xd73f, |
| 0x8f, 0xb1, 0x32, 0xf9, 0x0c, 0xa0, 0x42, 0xea); |
| |
| /* |
| * Setup unit test. |
| * |
| * @handle handle of the loaded image |
| * @systable system table |
| */ |
| static int setup(const efi_handle_t img_handle, |
| const struct efi_system_table *systable) |
| { |
| boottime = systable->boottime; |
| runtime = systable->runtime; |
| |
| return EFI_ST_SUCCESS; |
| } |
| |
| /* |
| * Execute unit test. |
| */ |
| static int execute(void) |
| { |
| efi_status_t ret; |
| efi_uintn_t len; |
| u32 attr; |
| u8 v[16] = {0x5d, 0xd1, 0x5e, 0x51, 0x5a, 0x05, 0xc7, 0x0c, |
| 0x35, 0x4a, 0xae, 0x87, 0xa5, 0xdf, 0x0f, 0x65,}; |
| u8 data[EFI_ST_MAX_DATA_SIZE]; |
| u16 varname[EFI_ST_MAX_VARNAME_SIZE]; |
| int flag; |
| efi_guid_t guid; |
| u64 max_storage, rem_storage, max_size; |
| |
| ret = runtime->query_variable_info(EFI_VARIABLE_BOOTSERVICE_ACCESS, |
| &max_storage, &rem_storage, |
| &max_size); |
| if (ret != EFI_SUCCESS) { |
| efi_st_todo("QueryVariableInfo failed\n"); |
| } else if (!max_storage || !rem_storage || !max_size) { |
| efi_st_error("QueryVariableInfo: wrong info\n"); |
| return EFI_ST_FAILURE; |
| } |
| /* Set variable 0 */ |
| ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0, |
| EFI_VARIABLE_BOOTSERVICE_ACCESS, |
| 3, v + 4); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("SetVariable failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| data[3] = 0xff; |
| len = 3; |
| ret = runtime->get_variable(u"efi_st_var0", &guid_vendor0, |
| &attr, &len, data); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("GetVariable failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (memcmp(data, v + 4, 3)) { |
| efi_st_error("GetVariable returned wrong value\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (data[3] != 0xff) { |
| efi_st_error("GetVariable wrote past the end of the buffer\n"); |
| return EFI_ST_FAILURE; |
| } |
| /* Set variable 1 */ |
| ret = runtime->set_variable(u"efi_st_var1", &guid_vendor1, |
| EFI_VARIABLE_BOOTSERVICE_ACCESS, |
| 8, v); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("SetVariable failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| len = EFI_ST_MAX_DATA_SIZE; |
| ret = runtime->get_variable(u"efi_st_var1", &guid_vendor1, |
| &attr, &len, data); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("GetVariable failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (len != 8) { |
| efi_st_error("GetVariable returned wrong length %u\n", |
| (unsigned int)len); |
| return EFI_ST_FAILURE; |
| } |
| if (memcmp(data, v, 8)) { |
| efi_st_error("GetVariable returned wrong value\n"); |
| return EFI_ST_FAILURE; |
| } |
| /* Append variable 1 */ |
| ret = runtime->set_variable(u"efi_st_var1", &guid_vendor1, |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | |
| EFI_VARIABLE_APPEND_WRITE, |
| 7, v + 8); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("SetVariable(APPEND_WRITE) failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| len = EFI_ST_MAX_DATA_SIZE; |
| ret = runtime->get_variable(u"efi_st_var1", &guid_vendor1, |
| &attr, &len, data); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("GetVariable failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (len != 15) |
| efi_st_todo("GetVariable returned wrong length %u\n", |
| (unsigned int)len); |
| if (memcmp(data, v, len)) |
| efi_st_todo("GetVariable returned wrong value\n"); |
| |
| /* Append variable 2, write to non-existent variable with datasize=0 */ |
| ret = runtime->set_variable(u"efi_none", &guid_vendor1, |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | |
| EFI_VARIABLE_APPEND_WRITE, |
| 0, v); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error( |
| "SetVariable(APPEND_WRITE) with size 0 to non-existent variable returns wrong code\n"); |
| return EFI_ST_FAILURE; |
| } |
| len = EFI_ST_MAX_DATA_SIZE; |
| ret = runtime->get_variable(u"efi_none", &guid_vendor1, |
| &attr, &len, data); |
| if (ret != EFI_NOT_FOUND) { |
| efi_st_error("Variable must not be created\n"); |
| return EFI_ST_FAILURE; |
| } |
| /* Append variable 2, write to non-existent variable with valid data size*/ |
| ret = runtime->set_variable(u"efi_none", &guid_vendor1, |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | |
| EFI_VARIABLE_APPEND_WRITE, |
| 15, v); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("SetVariable(APPEND_WRITE) with valid size and data to non-existent variable must be succcessful\n"); |
| return EFI_ST_FAILURE; |
| } |
| len = EFI_ST_MAX_DATA_SIZE; |
| ret = runtime->get_variable(u"efi_none", &guid_vendor1, |
| &attr, &len, data); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("GetVariable failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (len != 15) |
| efi_st_todo("GetVariable returned wrong length %u\n", |
| (unsigned int)len); |
| if (memcmp(data, v, len)) |
| efi_st_todo("GetVariable returned wrong value\n"); |
| /* Delete variable efi_none */ |
| ret = runtime->set_variable(u"efi_none", &guid_vendor1, |
| 0, 0, NULL); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("SetVariable failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| len = EFI_ST_MAX_DATA_SIZE; |
| ret = runtime->get_variable(u"efi_none", &guid_vendor1, |
| &attr, &len, data); |
| if (ret != EFI_NOT_FOUND) { |
| efi_st_error("Variable was not deleted\n"); |
| return EFI_ST_FAILURE; |
| } |
| /* Enumerate variables */ |
| |
| ret = runtime->get_next_variable_name(NULL, u"efi_st_var1", &guid); |
| if (ret != EFI_INVALID_PARAMETER) { |
| efi_st_error("GetNextVariableName missing parameter check\n"); |
| return EFI_ST_FAILURE; |
| } |
| |
| len = 24; |
| ret = runtime->get_next_variable_name(&len, NULL, &guid); |
| if (ret != EFI_INVALID_PARAMETER) { |
| efi_st_error("GetNextVariableName missing parameter check\n"); |
| return EFI_ST_FAILURE; |
| } |
| |
| len = 24; |
| ret = runtime->get_next_variable_name(&len, u"efi_st_var1", NULL); |
| if (ret != EFI_INVALID_PARAMETER) { |
| efi_st_error("GetNextVariableName missing parameter check\n"); |
| return EFI_ST_FAILURE; |
| } |
| |
| len = 1; |
| ret = runtime->get_next_variable_name(&len, u"", &guid); |
| if (ret != EFI_INVALID_PARAMETER) { |
| efi_st_error("GetNextVariableName missing parameter check\n"); |
| return EFI_ST_FAILURE; |
| } |
| |
| len = 16; |
| ret = runtime->get_next_variable_name(&len, u"efi_st_var1", &guid); |
| if (ret != EFI_INVALID_PARAMETER) { |
| efi_st_error("GetNextVariableName missing parameter check\n"); |
| return EFI_ST_FAILURE; |
| } |
| |
| boottime->set_mem(&guid, 16, 0); |
| *varname = 0; |
| flag = 0; |
| for (;;) { |
| len = EFI_ST_MAX_VARNAME_SIZE; |
| ret = runtime->get_next_variable_name(&len, varname, &guid); |
| if (ret == EFI_NOT_FOUND) |
| break; |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("GetNextVariableName failed (%u)\n", |
| (unsigned int)ret); |
| return EFI_ST_FAILURE; |
| } |
| if (!memcmp(&guid, &guid_vendor0, sizeof(efi_guid_t)) && |
| !efi_st_strcmp_16_8(varname, "efi_st_var0")) { |
| flag |= 1; |
| if (len != 24) { |
| efi_st_error("GetNextVariableName report wrong length %u, expected 24\n", |
| (unsigned int)len); |
| return EFI_ST_FAILURE; |
| } |
| } |
| if (!memcmp(&guid, &guid_vendor1, sizeof(efi_guid_t)) && |
| !efi_st_strcmp_16_8(varname, "efi_st_var1")) |
| flag |= 2; |
| } |
| if (flag != 3) { |
| efi_st_error( |
| "GetNextVariableName did not return all variables\n"); |
| return EFI_ST_FAILURE; |
| } |
| /* Delete variable 1 */ |
| ret = runtime->set_variable(u"efi_st_var1", &guid_vendor1, |
| 0, 0, NULL); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("SetVariable failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| len = EFI_ST_MAX_DATA_SIZE; |
| ret = runtime->get_variable(u"efi_st_var1", &guid_vendor1, |
| &attr, &len, data); |
| if (ret != EFI_NOT_FOUND) { |
| efi_st_error("Variable was not deleted\n"); |
| return EFI_ST_FAILURE; |
| } |
| /* Delete variable 0 */ |
| ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0, |
| 0, 0, NULL); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("SetVariable failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| len = EFI_ST_MAX_DATA_SIZE; |
| ret = runtime->get_variable(u"efi_st_var0", &guid_vendor0, |
| &attr, &len, data); |
| if (ret != EFI_NOT_FOUND) { |
| efi_st_error("Variable was not deleted\n"); |
| return EFI_ST_FAILURE; |
| } |
| |
| return EFI_ST_SUCCESS; |
| } |
| |
| EFI_UNIT_TEST(variables) = { |
| .name = "variables", |
| .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, |
| .setup = setup, |
| .execute = execute, |
| }; |