1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * EFI utils 4 * 5 * Copyright (c) 2017 Rob Clark 6 */ 7 8 #include <malloc.h> 9 #include <charset.h> 10 #include <efi_loader.h> 11 #include <hexdump.h> 12 13 #define READ_ONLY BIT(31) 14 15 /* 16 * Mapping between EFI variables and u-boot variables: 17 * 18 * efi_$guid_$varname = {attributes}(type)value 19 * 20 * For example: 21 * 22 * efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_OsIndicationsSupported= 23 * "{ro,boot,run}(blob)0000000000000000" 24 * efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_BootOrder= 25 * "(blob)00010000" 26 * 27 * The attributes are a comma separated list of these possible 28 * attributes: 29 * 30 * + ro - read-only 31 * + boot - boot-services access 32 * + run - runtime access 33 * 34 * NOTE: with current implementation, no variables are available after 35 * ExitBootServices, and all are persisted (if possible). 36 * 37 * If not specified, the attributes default to "{boot}". 38 * 39 * The required type is one of: 40 * 41 * + utf8 - raw utf8 string 42 * + blob - arbitrary length hex string 43 * 44 * Maybe a utf16 type would be useful to for a string value to be auto 45 * converted to utf16? 46 */ 47 48 #define PREFIX_LEN (strlen("efi_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx_")) 49 50 static efi_status_t efi_to_native(char **native, const u16 *variable_name, 51 const efi_guid_t *vendor) 52 { 53 size_t len; 54 char *pos; 55 56 len = PREFIX_LEN + utf16_utf8_strlen(variable_name) + 1; 57 *native = malloc(len); 58 if (!*native) 59 return EFI_OUT_OF_RESOURCES; 60 61 pos = *native; 62 pos += sprintf(pos, "efi_%pUl_", vendor); 63 utf16_utf8_strcpy(&pos, variable_name); 64 65 return EFI_SUCCESS; 66 } 67 68 static const char *prefix(const char *str, const char *prefix) 69 { 70 size_t n = strlen(prefix); 71 if (!strncmp(prefix, str, n)) 72 return str + n; 73 return NULL; 74 } 75 76 /* parse attributes part of variable value, if present: */ 77 static const char *parse_attr(const char *str, u32 *attrp) 78 { 79 u32 attr = 0; 80 char sep = '{'; 81 82 if (*str != '{') { 83 *attrp = EFI_VARIABLE_BOOTSERVICE_ACCESS; 84 return str; 85 } 86 87 while (*str == sep) { 88 const char *s; 89 90 str++; 91 92 if ((s = prefix(str, "ro"))) { 93 attr |= READ_ONLY; 94 } else if ((s = prefix(str, "boot"))) { 95 attr |= EFI_VARIABLE_BOOTSERVICE_ACCESS; 96 } else if ((s = prefix(str, "run"))) { 97 attr |= EFI_VARIABLE_RUNTIME_ACCESS; 98 } else { 99 printf("invalid attribute: %s\n", str); 100 break; 101 } 102 103 str = s; 104 sep = ','; 105 } 106 107 str++; 108 109 *attrp = attr; 110 111 return str; 112 } 113 114 /* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetVariable.28.29 */ 115 efi_status_t EFIAPI efi_get_variable(u16 *variable_name, 116 const efi_guid_t *vendor, u32 *attributes, 117 efi_uintn_t *data_size, void *data) 118 { 119 char *native_name; 120 efi_status_t ret; 121 unsigned long in_size; 122 const char *val, *s; 123 u32 attr; 124 125 EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes, 126 data_size, data); 127 128 if (!variable_name || !vendor || !data_size) 129 return EFI_EXIT(EFI_INVALID_PARAMETER); 130 131 ret = efi_to_native(&native_name, variable_name, vendor); 132 if (ret) 133 return EFI_EXIT(ret); 134 135 debug("%s: get '%s'\n", __func__, native_name); 136 137 val = env_get(native_name); 138 free(native_name); 139 if (!val) 140 return EFI_EXIT(EFI_NOT_FOUND); 141 142 val = parse_attr(val, &attr); 143 144 in_size = *data_size; 145 146 if ((s = prefix(val, "(blob)"))) { 147 size_t len = strlen(s); 148 149 /* number of hexadecimal digits must be even */ 150 if (len & 1) 151 return EFI_EXIT(EFI_DEVICE_ERROR); 152 153 /* two characters per byte: */ 154 len /= 2; 155 *data_size = len; 156 157 if (in_size < len) 158 return EFI_EXIT(EFI_BUFFER_TOO_SMALL); 159 160 if (!data) 161 return EFI_EXIT(EFI_INVALID_PARAMETER); 162 163 if (hex2bin(data, s, len)) 164 return EFI_EXIT(EFI_DEVICE_ERROR); 165 166 debug("%s: got value: \"%s\"\n", __func__, s); 167 } else if ((s = prefix(val, "(utf8)"))) { 168 unsigned len = strlen(s) + 1; 169 170 *data_size = len; 171 172 if (in_size < len) 173 return EFI_EXIT(EFI_BUFFER_TOO_SMALL); 174 175 if (!data) 176 return EFI_EXIT(EFI_INVALID_PARAMETER); 177 178 memcpy(data, s, len); 179 ((char *)data)[len] = '\0'; 180 181 debug("%s: got value: \"%s\"\n", __func__, (char *)data); 182 } else { 183 debug("%s: invalid value: '%s'\n", __func__, val); 184 return EFI_EXIT(EFI_DEVICE_ERROR); 185 } 186 187 if (attributes) 188 *attributes = attr & EFI_VARIABLE_MASK; 189 190 return EFI_EXIT(EFI_SUCCESS); 191 } 192 193 /* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetNextVariableName.28.29 */ 194 efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size, 195 u16 *variable_name, 196 const efi_guid_t *vendor) 197 { 198 EFI_ENTRY("%p \"%ls\" %pUl", variable_name_size, variable_name, vendor); 199 200 return EFI_EXIT(EFI_DEVICE_ERROR); 201 } 202 203 /* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#SetVariable.28.29 */ 204 efi_status_t EFIAPI efi_set_variable(u16 *variable_name, 205 const efi_guid_t *vendor, u32 attributes, 206 efi_uintn_t data_size, const void *data) 207 { 208 char *native_name = NULL, *val = NULL, *s; 209 efi_status_t ret = EFI_SUCCESS; 210 u32 attr; 211 212 EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes, 213 data_size, data); 214 215 if (!variable_name || !vendor) { 216 ret = EFI_INVALID_PARAMETER; 217 goto out; 218 } 219 220 ret = efi_to_native(&native_name, variable_name, vendor); 221 if (ret) 222 goto out; 223 224 #define ACCESS_ATTR (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS) 225 226 if ((data_size == 0) || !(attributes & ACCESS_ATTR)) { 227 /* delete the variable: */ 228 env_set(native_name, NULL); 229 ret = EFI_SUCCESS; 230 goto out; 231 } 232 233 val = env_get(native_name); 234 if (val) { 235 parse_attr(val, &attr); 236 237 if (attr & READ_ONLY) { 238 /* We should not free val */ 239 val = NULL; 240 ret = EFI_WRITE_PROTECTED; 241 goto out; 242 } 243 } 244 245 val = malloc(2 * data_size + strlen("{ro,run,boot}(blob)") + 1); 246 if (!val) { 247 ret = EFI_OUT_OF_RESOURCES; 248 goto out; 249 } 250 251 s = val; 252 253 /* store attributes: */ 254 attributes &= (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS); 255 s += sprintf(s, "{"); 256 while (attributes) { 257 u32 attr = 1 << (ffs(attributes) - 1); 258 259 if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS) 260 s += sprintf(s, "boot"); 261 else if (attr == EFI_VARIABLE_RUNTIME_ACCESS) 262 s += sprintf(s, "run"); 263 264 attributes &= ~attr; 265 if (attributes) 266 s += sprintf(s, ","); 267 } 268 s += sprintf(s, "}"); 269 270 /* store payload: */ 271 s += sprintf(s, "(blob)"); 272 s = bin2hex(s, data, data_size); 273 *s = '\0'; 274 275 debug("%s: setting: %s=%s\n", __func__, native_name, val); 276 277 if (env_set(native_name, val)) 278 ret = EFI_DEVICE_ERROR; 279 280 out: 281 free(native_name); 282 free(val); 283 284 return EFI_EXIT(ret); 285 } 286