1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * efi_selftest_variables_runtime 4 * 5 * Copyright (c) 2019 Heinrich Schuchardt <xypron.glpk@gmx.de> 6 * 7 * This unit test checks the runtime services for variables after 8 * ExitBootServices(): 9 * GetVariable, GetNextVariableName, SetVariable, QueryVariableInfo. 10 */ 11 12#include <efi_selftest.h> 13#include <efi_variable.h> 14#include <u-boot/crc.h> 15 16#define EFI_ST_MAX_DATA_SIZE 16 17#define EFI_ST_MAX_VARNAME_SIZE 40 18 19static struct efi_boot_services *boottime; 20static struct efi_runtime_services *runtime; 21static const efi_guid_t guid_vendor0 = EFI_GLOBAL_VARIABLE_GUID; 22static const efi_guid_t __efi_runtime_data efi_rt_var_guid = 23 U_BOOT_EFI_RT_VAR_FILE_GUID; 24 25/* 26 * Setup unit test. 27 * 28 * @handle handle of the loaded image 29 * @systable system table 30 */ 31static int setup(const efi_handle_t img_handle, 32 const struct efi_system_table *systable) 33{ 34 boottime = systable->boottime; 35 runtime = systable->runtime; 36 37 return EFI_ST_SUCCESS; 38} 39 40/** 41 * execute() - execute unit test 42 * 43 * As runtime support is not implmented expect EFI_UNSUPPORTED to be returned. 44 */ 45static int execute(void) 46{ 47 efi_status_t ret; 48 efi_uintn_t len, avail, append_len = 17; 49 u32 attr; 50 u8 v[16] = {0x5d, 0xd1, 0x5e, 0x51, 0x5a, 0x05, 0xc7, 0x0c, 51 0x35, 0x4a, 0xae, 0x87, 0xa5, 0xdf, 0x0f, 0x65,}; 52 u8 v2[CONFIG_EFI_VAR_BUF_SIZE]; 53 u8 data[EFI_ST_MAX_DATA_SIZE]; 54 u8 data2[CONFIG_EFI_VAR_BUF_SIZE]; 55 u16 varname[EFI_ST_MAX_VARNAME_SIZE]; 56 efi_guid_t guid; 57 u64 max_storage, rem_storage, max_size; 58 int test_ret; 59 60 memset(v2, 0x1, sizeof(v2)); 61 62 if (IS_ENABLED(CONFIG_EFI_VARIABLE_FILE_STORE)) { 63 test_ret = efi_st_query_variable_common(runtime, EFI_VARIABLE_BOOTSERVICE_ACCESS | 64 EFI_VARIABLE_RUNTIME_ACCESS); 65 if (test_ret != EFI_ST_SUCCESS) { 66 efi_st_error("QueryVariableInfo failed\n"); 67 return EFI_ST_FAILURE; 68 } 69 } else { 70 ret = runtime->query_variable_info(EFI_VARIABLE_BOOTSERVICE_ACCESS, 71 &max_storage, &rem_storage, 72 &max_size); 73 if (ret != EFI_UNSUPPORTED) { 74 efi_st_error("QueryVariableInfo failed\n"); 75 return EFI_ST_FAILURE; 76 } 77 } 78 79 ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0, 80 EFI_VARIABLE_BOOTSERVICE_ACCESS | 81 EFI_VARIABLE_RUNTIME_ACCESS, 82 3, v + 4); 83 if (IS_ENABLED(CONFIG_EFI_RT_VOLATILE_STORE)) { 84 efi_uintn_t prev_len, delta; 85 struct efi_var_entry *var; 86 struct efi_var_file *hdr; 87 88 /* At runtime only non-volatile variables may be set. */ 89 if (ret != EFI_INVALID_PARAMETER) { 90 efi_st_error("SetVariable failed\n"); 91 return EFI_ST_FAILURE; 92 } 93 94 /* runtime atttribute must be set */ 95 ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0, 96 EFI_VARIABLE_BOOTSERVICE_ACCESS | 97 EFI_VARIABLE_NON_VOLATILE, 98 3, v + 4); 99 if (ret != EFI_INVALID_PARAMETER) { 100 efi_st_error("SetVariable failed\n"); 101 return EFI_ST_FAILURE; 102 } 103 104 len = sizeof(data); 105 ret = runtime->get_variable(u"RTStorageVolatile", 106 &efi_rt_var_guid, 107 &attr, &len, data); 108 if (ret != EFI_SUCCESS) { 109 efi_st_error("GetVariable failed\n"); 110 return EFI_ST_FAILURE; 111 } 112 113 if (len != sizeof(EFI_VAR_FILE_NAME) || 114 memcmp(data, EFI_VAR_FILE_NAME, sizeof(EFI_VAR_FILE_NAME))) { 115 data[len - 1] = 0; 116 efi_st_error("RTStorageVolatile = %s\n", data); 117 return EFI_ST_FAILURE; 118 } 119 120 len = sizeof(data2); 121 ret = runtime->get_variable(u"VarToFile", &efi_rt_var_guid, 122 &attr, &len, data2); 123 if (ret != EFI_SUCCESS) { 124 efi_st_error("GetVariable failed\n"); 125 return EFI_ST_FAILURE; 126 } 127 /* 128 * VarToFile size must change once a variable is inserted 129 * Store it now, we'll use it later 130 */ 131 prev_len = len; 132 ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0, 133 EFI_VARIABLE_BOOTSERVICE_ACCESS | 134 EFI_VARIABLE_RUNTIME_ACCESS | 135 EFI_VARIABLE_NON_VOLATILE, 136 sizeof(v2), 137 v2); 138 /* 139 * This will try to update VarToFile as well and must fail, 140 * without changing or deleting VarToFile 141 */ 142 if (ret != EFI_OUT_OF_RESOURCES) { 143 efi_st_error("SetVariable failed\n"); 144 return EFI_ST_FAILURE; 145 } 146 len = sizeof(data2); 147 ret = runtime->get_variable(u"VarToFile", &efi_rt_var_guid, 148 &attr, &len, data2); 149 if (ret != EFI_SUCCESS || prev_len != len) { 150 efi_st_error("Get/SetVariable failed\n"); 151 return EFI_ST_FAILURE; 152 } 153 154 /* Add an 8byte aligned variable */ 155 ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0, 156 EFI_VARIABLE_BOOTSERVICE_ACCESS | 157 EFI_VARIABLE_RUNTIME_ACCESS | 158 EFI_VARIABLE_NON_VOLATILE, 159 sizeof(v), v); 160 if (ret != EFI_SUCCESS) { 161 efi_st_error("SetVariable failed\n"); 162 return EFI_ST_FAILURE; 163 } 164 165 /* Delete it by setting the attrs to 0 */ 166 ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0, 167 0, sizeof(v), v); 168 if (ret != EFI_SUCCESS) { 169 efi_st_error("SetVariable failed\n"); 170 return EFI_ST_FAILURE; 171 } 172 173 /* Add it back */ 174 ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0, 175 EFI_VARIABLE_BOOTSERVICE_ACCESS | 176 EFI_VARIABLE_RUNTIME_ACCESS | 177 EFI_VARIABLE_NON_VOLATILE, 178 sizeof(v), v); 179 if (ret != EFI_SUCCESS) { 180 efi_st_error("SetVariable failed\n"); 181 return EFI_ST_FAILURE; 182 } 183 184 /* Delete it again by setting the size to 0 */ 185 ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0, 186 EFI_VARIABLE_BOOTSERVICE_ACCESS | 187 EFI_VARIABLE_RUNTIME_ACCESS | 188 EFI_VARIABLE_NON_VOLATILE, 189 0, NULL); 190 if (ret != EFI_SUCCESS) { 191 efi_st_error("SetVariable failed\n"); 192 return EFI_ST_FAILURE; 193 } 194 195 /* Delete it again and make sure it's not there */ 196 ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0, 197 EFI_VARIABLE_BOOTSERVICE_ACCESS | 198 EFI_VARIABLE_RUNTIME_ACCESS | 199 EFI_VARIABLE_NON_VOLATILE, 200 0, NULL); 201 if (ret != EFI_NOT_FOUND) { 202 efi_st_error("SetVariable failed\n"); 203 return EFI_ST_FAILURE; 204 } 205 206 /* 207 * Add a non-aligned variable 208 * VarToFile updates must include efi_st_var0 209 */ 210 ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0, 211 EFI_VARIABLE_BOOTSERVICE_ACCESS | 212 EFI_VARIABLE_RUNTIME_ACCESS | 213 EFI_VARIABLE_NON_VOLATILE, 214 9, v + 4); 215 if (ret != EFI_SUCCESS) { 216 efi_st_error("SetVariable failed\n"); 217 return EFI_ST_FAILURE; 218 } 219 var = efi_var_mem_find(&guid_vendor0, u"efi_st_var0", NULL); 220 if (!var) { 221 efi_st_error("GetVariable failed\n"); 222 return EFI_ST_FAILURE; 223 } 224 delta = efi_var_entry_len(var); 225 len = sizeof(data2); 226 ret = runtime->get_variable(u"VarToFile", &efi_rt_var_guid, 227 &attr, &len, data2); 228 if (ret != EFI_SUCCESS || prev_len + delta != len) { 229 efi_st_error("Get/SetVariable failed\n"); 230 return EFI_ST_FAILURE; 231 } 232 233 /* 234 * Append on an existing variable must update VarToFile 235 * Our variable entries are 8-byte aligned. 236 * Adding a single byte will fit on the existing space 237 */ 238 prev_len = len; 239 avail = efi_var_entry_len(var) - 240 (sizeof(u16) * (u16_strlen(var->name) + 1) + sizeof(*var)) - 241 var->length; 242 if (avail >= append_len) 243 delta = 0; 244 else 245 delta = ALIGN(append_len - avail, 8); 246 ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0, 247 EFI_VARIABLE_BOOTSERVICE_ACCESS | 248 EFI_VARIABLE_RUNTIME_ACCESS | 249 EFI_VARIABLE_APPEND_WRITE | 250 EFI_VARIABLE_NON_VOLATILE, 251 append_len, v2); 252 if (ret != EFI_SUCCESS) { 253 efi_st_error("SetVariable failed\n"); 254 return EFI_ST_FAILURE; 255 } 256 len = sizeof(data2); 257 ret = runtime->get_variable(u"VarToFile", &efi_rt_var_guid, 258 &attr, &len, data2); 259 if (ret != EFI_SUCCESS) { 260 efi_st_error("GetVariable failed\n"); 261 return EFI_ST_FAILURE; 262 } 263 if (prev_len + delta != len) { 264 efi_st_error("Unexpected VarToFile size"); 265 return EFI_ST_FAILURE; 266 } 267 268 /* Make sure that variable contains a valid file */ 269 hdr = (struct efi_var_file *)data2; 270 if (hdr->magic != EFI_VAR_FILE_MAGIC || 271 len != hdr->length || 272 hdr->crc32 != crc32(0, (u8 *)((uintptr_t)data2 + sizeof(struct efi_var_file)), 273 len - sizeof(struct efi_var_file))) { 274 efi_st_error("VarToFile invalid header\n"); 275 return EFI_ST_FAILURE; 276 } 277 278 /* Variables that are BS, RT and volatile are RO after EBS */ 279 ret = runtime->set_variable(u"VarToFile", &efi_rt_var_guid, 280 EFI_VARIABLE_BOOTSERVICE_ACCESS | 281 EFI_VARIABLE_RUNTIME_ACCESS | 282 EFI_VARIABLE_NON_VOLATILE, 283 sizeof(v), v); 284 if (ret != EFI_WRITE_PROTECTED) { 285 efi_st_error("Get/SetVariable failed\n"); 286 return EFI_ST_FAILURE; 287 } 288 } else { 289 if (ret != EFI_UNSUPPORTED) { 290 efi_st_error("SetVariable failed\n"); 291 return EFI_ST_FAILURE; 292 } 293 } 294 len = EFI_ST_MAX_DATA_SIZE; 295 ret = runtime->get_variable(u"PlatformLangCodes", &guid_vendor0, 296 &attr, &len, data); 297 if (ret != EFI_SUCCESS) { 298 efi_st_error("GetVariable failed\n"); 299 return EFI_ST_FAILURE; 300 } 301 memset(&guid, 0, 16); 302 *varname = 0; 303 len = 2 * EFI_ST_MAX_VARNAME_SIZE; 304 ret = runtime->get_next_variable_name(&len, varname, &guid); 305 if (ret != EFI_SUCCESS) { 306 efi_st_error("GetNextVariableName failed\n"); 307 return EFI_ST_FAILURE; 308 } 309 310 return EFI_ST_SUCCESS; 311} 312 313EFI_UNIT_TEST(variables_run) = { 314 .name = "variables at runtime", 315 .phase = EFI_SETUP_BEFORE_BOOTTIME_EXIT, 316 .setup = setup, 317 .execute = execute, 318}; 319