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(int 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 int hex2mem(u8 *mem, const char *hexstr, int size) 64 { 65 int nibble; 66 int i; 67 68 for (i = 0; i < size; i++) { 69 if (*hexstr == '\0') 70 break; 71 72 nibble = hex(*hexstr); 73 if (nibble < 0) 74 return -1; 75 76 *mem = nibble; 77 hexstr++; 78 79 nibble = hex(*hexstr); 80 if (nibble < 0) 81 return -1; 82 83 *mem = (*mem << 4) | nibble; 84 hexstr++; 85 mem++; 86 } 87 88 return i; 89 } 90 91 static char *mem2hex(char *hexstr, const u8 *mem, int count) 92 { 93 static const char hexchars[] = "0123456789abcdef"; 94 95 while (count-- > 0) { 96 u8 ch = *mem++; 97 *hexstr++ = hexchars[ch >> 4]; 98 *hexstr++ = hexchars[ch & 0xf]; 99 } 100 101 return hexstr; 102 } 103 104 static efi_status_t efi_to_native(char *native, u16 *variable_name, 105 efi_guid_t *vendor) 106 { 107 size_t len; 108 109 len = utf16_strlen((u16 *)variable_name); 110 if (len >= MAX_VAR_NAME) 111 return EFI_DEVICE_ERROR; 112 113 native += sprintf(native, "efi_%pUl_", vendor); 114 native = (char *)utf16_to_utf8((u8 *)native, (u16 *)variable_name, len); 115 *native = '\0'; 116 117 return EFI_SUCCESS; 118 } 119 120 static const char *prefix(const char *str, const char *prefix) 121 { 122 size_t n = strlen(prefix); 123 if (!strncmp(prefix, str, n)) 124 return str + n; 125 return NULL; 126 } 127 128 /* parse attributes part of variable value, if present: */ 129 static const char *parse_attr(const char *str, u32 *attrp) 130 { 131 u32 attr = 0; 132 char sep = '{'; 133 134 if (*str != '{') { 135 *attrp = EFI_VARIABLE_BOOTSERVICE_ACCESS; 136 return str; 137 } 138 139 while (*str == sep) { 140 const char *s; 141 142 str++; 143 144 if ((s = prefix(str, "ro"))) { 145 attr |= READ_ONLY; 146 } else if ((s = prefix(str, "boot"))) { 147 attr |= EFI_VARIABLE_BOOTSERVICE_ACCESS; 148 } else if ((s = prefix(str, "run"))) { 149 attr |= EFI_VARIABLE_RUNTIME_ACCESS; 150 } else { 151 printf("invalid attribute: %s\n", str); 152 break; 153 } 154 155 str = s; 156 sep = ','; 157 } 158 159 str++; 160 161 *attrp = attr; 162 163 return str; 164 } 165 166 /* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetVariable.28.29 */ 167 efi_status_t EFIAPI efi_get_variable(u16 *variable_name, efi_guid_t *vendor, 168 u32 *attributes, efi_uintn_t *data_size, 169 void *data) 170 { 171 char native_name[MAX_NATIVE_VAR_NAME + 1]; 172 efi_status_t ret; 173 unsigned long in_size; 174 const char *val, *s; 175 u32 attr; 176 177 EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes, 178 data_size, data); 179 180 if (!variable_name || !vendor || !data_size) 181 return EFI_EXIT(EFI_INVALID_PARAMETER); 182 183 ret = efi_to_native(native_name, variable_name, vendor); 184 if (ret) 185 return EFI_EXIT(ret); 186 187 debug("%s: get '%s'\n", __func__, native_name); 188 189 val = env_get(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[MAX_NATIVE_VAR_NAME + 1]; 260 efi_status_t ret = EFI_SUCCESS; 261 char *val, *s; 262 u32 attr; 263 264 EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes, 265 data_size, data); 266 267 if (!variable_name || !vendor) 268 return EFI_EXIT(EFI_INVALID_PARAMETER); 269 270 ret = efi_to_native(native_name, variable_name, vendor); 271 if (ret) 272 return EFI_EXIT(ret); 273 274 #define ACCESS_ATTR (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS) 275 276 if ((data_size == 0) || !(attributes & ACCESS_ATTR)) { 277 /* delete the variable: */ 278 env_set(native_name, NULL); 279 return EFI_EXIT(EFI_SUCCESS); 280 } 281 282 val = env_get(native_name); 283 if (val) { 284 parse_attr(val, &attr); 285 286 if (attr & READ_ONLY) 287 return EFI_EXIT(EFI_WRITE_PROTECTED); 288 } 289 290 val = malloc(2 * data_size + strlen("{ro,run,boot}(blob)") + 1); 291 if (!val) 292 return EFI_EXIT(EFI_OUT_OF_RESOURCES); 293 294 s = val; 295 296 /* store attributes: */ 297 attributes &= (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS); 298 s += sprintf(s, "{"); 299 while (attributes) { 300 u32 attr = 1 << (ffs(attributes) - 1); 301 302 if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS) 303 s += sprintf(s, "boot"); 304 else if (attr == EFI_VARIABLE_RUNTIME_ACCESS) 305 s += sprintf(s, "run"); 306 307 attributes &= ~attr; 308 if (attributes) 309 s += sprintf(s, ","); 310 } 311 s += sprintf(s, "}"); 312 313 /* store payload: */ 314 s += sprintf(s, "(blob)"); 315 s = mem2hex(s, data, data_size); 316 *s = '\0'; 317 318 debug("%s: setting: %s=%s\n", __func__, native_name, val); 319 320 if (env_set(native_name, val)) 321 ret = EFI_DEVICE_ERROR; 322 323 free(val); 324 325 return EFI_EXIT(ret); 326 } 327