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 MAX_VAR_NAME 31 48 #define MAX_NATIVE_VAR_NAME \ 49 (strlen("efi_xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx_") + \ 50 (MAX_VAR_NAME * MAX_UTF8_PER_UTF16)) 51 52 static int hex(unsigned char ch) 53 { 54 if (ch >= 'a' && ch <= 'f') 55 return ch-'a'+10; 56 if (ch >= '0' && ch <= '9') 57 return ch-'0'; 58 if (ch >= 'A' && ch <= 'F') 59 return ch-'A'+10; 60 return -1; 61 } 62 63 static const char *hex2mem(u8 *mem, const char *hexstr, int count) 64 { 65 memset(mem, 0, count/2); 66 67 do { 68 int nibble; 69 70 *mem = 0; 71 72 if (!count || !*hexstr) 73 break; 74 75 nibble = hex(*hexstr); 76 if (nibble < 0) 77 break; 78 79 *mem = nibble; 80 count--; 81 hexstr++; 82 83 if (!count || !*hexstr) 84 break; 85 86 nibble = hex(*hexstr); 87 if (nibble < 0) 88 break; 89 90 *mem = (*mem << 4) | nibble; 91 count--; 92 hexstr++; 93 mem++; 94 95 } while (1); 96 97 if (*hexstr) 98 return hexstr; 99 100 return NULL; 101 } 102 103 static char *mem2hex(char *hexstr, const u8 *mem, int count) 104 { 105 static const char hexchars[] = "0123456789abcdef"; 106 107 while (count-- > 0) { 108 u8 ch = *mem++; 109 *hexstr++ = hexchars[ch >> 4]; 110 *hexstr++ = hexchars[ch & 0xf]; 111 } 112 113 return hexstr; 114 } 115 116 static efi_status_t efi_to_native(char *native, s16 *variable_name, 117 efi_guid_t *vendor) 118 { 119 size_t len; 120 121 len = utf16_strlen((u16 *)variable_name); 122 if (len >= MAX_VAR_NAME) 123 return EFI_DEVICE_ERROR; 124 125 native += sprintf(native, "efi_%pUl_", vendor); 126 native = (char *)utf16_to_utf8((u8 *)native, (u16 *)variable_name, len); 127 *native = '\0'; 128 129 return EFI_SUCCESS; 130 } 131 132 static const char *prefix(const char *str, const char *prefix) 133 { 134 size_t n = strlen(prefix); 135 if (!strncmp(prefix, str, n)) 136 return str + n; 137 return NULL; 138 } 139 140 /* parse attributes part of variable value, if present: */ 141 static const char *parse_attr(const char *str, u32 *attrp) 142 { 143 u32 attr = 0; 144 char sep = '{'; 145 146 if (*str != '{') { 147 *attrp = EFI_VARIABLE_BOOTSERVICE_ACCESS; 148 return str; 149 } 150 151 while (*str == sep) { 152 const char *s; 153 154 str++; 155 156 if ((s = prefix(str, "ro"))) { 157 attr |= READ_ONLY; 158 } else if ((s = prefix(str, "boot"))) { 159 attr |= EFI_VARIABLE_BOOTSERVICE_ACCESS; 160 } else if ((s = prefix(str, "run"))) { 161 attr |= EFI_VARIABLE_RUNTIME_ACCESS; 162 } else { 163 printf("invalid attribute: %s\n", str); 164 break; 165 } 166 167 str = s; 168 sep = ','; 169 } 170 171 str++; 172 173 *attrp = attr; 174 175 return str; 176 } 177 178 /* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetVariable.28.29 */ 179 efi_status_t EFIAPI efi_get_variable(s16 *variable_name, 180 efi_guid_t *vendor, u32 *attributes, 181 unsigned long *data_size, void *data) 182 { 183 char native_name[MAX_NATIVE_VAR_NAME + 1]; 184 efi_status_t ret; 185 unsigned long in_size; 186 const char *val, *s; 187 u32 attr; 188 189 EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes, 190 data_size, data); 191 192 if (!variable_name || !vendor || !data_size) 193 return EFI_EXIT(EFI_INVALID_PARAMETER); 194 195 ret = efi_to_native(native_name, variable_name, vendor); 196 if (ret) 197 return EFI_EXIT(ret); 198 199 debug("%s: get '%s'\n", __func__, native_name); 200 201 val = env_get(native_name); 202 if (!val) 203 return EFI_EXIT(EFI_NOT_FOUND); 204 205 val = parse_attr(val, &attr); 206 207 in_size = *data_size; 208 209 if ((s = prefix(val, "(blob)"))) { 210 unsigned len = strlen(s); 211 212 /* two characters per byte: */ 213 len = DIV_ROUND_UP(len, 2); 214 *data_size = len; 215 216 if (in_size < len) 217 return EFI_EXIT(EFI_BUFFER_TOO_SMALL); 218 219 if (!data) 220 return EFI_EXIT(EFI_INVALID_PARAMETER); 221 222 if (hex2mem(data, s, len * 2)) 223 return EFI_EXIT(EFI_DEVICE_ERROR); 224 225 debug("%s: got value: \"%s\"\n", __func__, s); 226 } else if ((s = prefix(val, "(utf8)"))) { 227 unsigned len = strlen(s) + 1; 228 229 *data_size = len; 230 231 if (in_size < len) 232 return EFI_EXIT(EFI_BUFFER_TOO_SMALL); 233 234 if (!data) 235 return EFI_EXIT(EFI_INVALID_PARAMETER); 236 237 memcpy(data, s, len); 238 ((char *)data)[len] = '\0'; 239 240 debug("%s: got value: \"%s\"\n", __func__, (char *)data); 241 } else { 242 debug("%s: invalid value: '%s'\n", __func__, val); 243 return EFI_EXIT(EFI_DEVICE_ERROR); 244 } 245 246 if (attributes) 247 *attributes = attr & EFI_VARIABLE_MASK; 248 249 return EFI_EXIT(EFI_SUCCESS); 250 } 251 252 /* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetNextVariableName.28.29 */ 253 efi_status_t EFIAPI efi_get_next_variable( 254 unsigned long *variable_name_size, 255 s16 *variable_name, efi_guid_t *vendor) 256 { 257 EFI_ENTRY("%p \"%ls\" %pUl", variable_name_size, variable_name, vendor); 258 259 return EFI_EXIT(EFI_DEVICE_ERROR); 260 } 261 262 /* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#SetVariable.28.29 */ 263 efi_status_t EFIAPI efi_set_variable(s16 *variable_name, 264 efi_guid_t *vendor, u32 attributes, 265 unsigned long data_size, void *data) 266 { 267 char native_name[MAX_NATIVE_VAR_NAME + 1]; 268 efi_status_t ret = EFI_SUCCESS; 269 char *val, *s; 270 u32 attr; 271 272 EFI_ENTRY("\"%ls\" %pUl %x %lu %p", variable_name, vendor, attributes, 273 data_size, data); 274 275 if (!variable_name || !vendor) 276 return EFI_EXIT(EFI_INVALID_PARAMETER); 277 278 ret = efi_to_native(native_name, variable_name, vendor); 279 if (ret) 280 return EFI_EXIT(ret); 281 282 #define ACCESS_ATTR (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS) 283 284 if ((data_size == 0) || !(attributes & ACCESS_ATTR)) { 285 /* delete the variable: */ 286 env_set(native_name, NULL); 287 return EFI_EXIT(EFI_SUCCESS); 288 } 289 290 val = env_get(native_name); 291 if (val) { 292 parse_attr(val, &attr); 293 294 if (attr & READ_ONLY) 295 return EFI_EXIT(EFI_WRITE_PROTECTED); 296 } 297 298 val = malloc(2 * data_size + strlen("{ro,run,boot}(blob)") + 1); 299 if (!val) 300 return EFI_EXIT(EFI_OUT_OF_RESOURCES); 301 302 s = val; 303 304 /* store attributes: */ 305 attributes &= (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS); 306 s += sprintf(s, "{"); 307 while (attributes) { 308 u32 attr = 1 << (ffs(attributes) - 1); 309 310 if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS) 311 s += sprintf(s, "boot"); 312 else if (attr == EFI_VARIABLE_RUNTIME_ACCESS) 313 s += sprintf(s, "run"); 314 315 attributes &= ~attr; 316 if (attributes) 317 s += sprintf(s, ","); 318 } 319 s += sprintf(s, "}"); 320 321 /* store payload: */ 322 s += sprintf(s, "(blob)"); 323 s = mem2hex(s, data, data_size); 324 *s = '\0'; 325 326 debug("%s: setting: %s=%s\n", __func__, native_name, val); 327 328 if (env_set(native_name, val)) 329 ret = EFI_DEVICE_ERROR; 330 331 free(val); 332 333 return EFI_EXIT(ret); 334 } 335