efi_variable.c 7.0 KB

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