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