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