efi_variable.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  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(unsigned char 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 const char *hex2mem(u8 *mem, const char *hexstr, int count)
  58. {
  59. memset(mem, 0, count/2);
  60. do {
  61. int nibble;
  62. *mem = 0;
  63. if (!count || !*hexstr)
  64. break;
  65. nibble = hex(*hexstr);
  66. if (nibble < 0)
  67. break;
  68. *mem = nibble;
  69. count--;
  70. hexstr++;
  71. if (!count || !*hexstr)
  72. break;
  73. nibble = hex(*hexstr);
  74. if (nibble < 0)
  75. break;
  76. *mem = (*mem << 4) | nibble;
  77. count--;
  78. hexstr++;
  79. mem++;
  80. } while (1);
  81. if (*hexstr)
  82. return hexstr;
  83. return NULL;
  84. }
  85. static char *mem2hex(char *hexstr, const u8 *mem, int count)
  86. {
  87. static const char hexchars[] = "0123456789abcdef";
  88. while (count-- > 0) {
  89. u8 ch = *mem++;
  90. *hexstr++ = hexchars[ch >> 4];
  91. *hexstr++ = hexchars[ch & 0xf];
  92. }
  93. return hexstr;
  94. }
  95. static efi_status_t efi_to_native(char *native, s16 *variable_name,
  96. efi_guid_t *vendor)
  97. {
  98. size_t len;
  99. len = utf16_strlen((u16 *)variable_name);
  100. if (len >= MAX_VAR_NAME)
  101. return EFI_DEVICE_ERROR;
  102. native += sprintf(native, "efi_%pUl_", vendor);
  103. native = (char *)utf16_to_utf8((u8 *)native, (u16 *)variable_name, len);
  104. *native = '\0';
  105. return EFI_SUCCESS;
  106. }
  107. static const char *prefix(const char *str, const char *prefix)
  108. {
  109. size_t n = strlen(prefix);
  110. if (!strncmp(prefix, str, n))
  111. return str + n;
  112. return NULL;
  113. }
  114. /* parse attributes part of variable value, if present: */
  115. static const char *parse_attr(const char *str, u32 *attrp)
  116. {
  117. u32 attr = 0;
  118. char sep = '{';
  119. if (*str != '{') {
  120. *attrp = EFI_VARIABLE_BOOTSERVICE_ACCESS;
  121. return str;
  122. }
  123. while (*str == sep) {
  124. const char *s;
  125. str++;
  126. if ((s = prefix(str, "ro"))) {
  127. attr |= READ_ONLY;
  128. } else if ((s = prefix(str, "boot"))) {
  129. attr |= EFI_VARIABLE_BOOTSERVICE_ACCESS;
  130. } else if ((s = prefix(str, "run"))) {
  131. attr |= EFI_VARIABLE_RUNTIME_ACCESS;
  132. } else {
  133. printf("invalid attribute: %s\n", str);
  134. break;
  135. }
  136. str = s;
  137. sep = ',';
  138. }
  139. str++;
  140. *attrp = attr;
  141. return str;
  142. }
  143. /* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetVariable.28.29 */
  144. efi_status_t EFIAPI efi_get_variable(s16 *variable_name,
  145. efi_guid_t *vendor, u32 *attributes,
  146. unsigned long *data_size, void *data)
  147. {
  148. char native_name[MAX_NATIVE_VAR_NAME + 1];
  149. efi_status_t ret;
  150. unsigned long in_size;
  151. const char *val, *s;
  152. u32 attr;
  153. EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes,
  154. data_size, data);
  155. if (!variable_name || !vendor || !data_size)
  156. return EFI_EXIT(EFI_INVALID_PARAMETER);
  157. ret = efi_to_native(native_name, variable_name, vendor);
  158. if (ret)
  159. return EFI_EXIT(ret);
  160. debug("%s: get '%s'\n", __func__, native_name);
  161. val = env_get(native_name);
  162. if (!val)
  163. return EFI_EXIT(EFI_NOT_FOUND);
  164. val = parse_attr(val, &attr);
  165. in_size = *data_size;
  166. if ((s = prefix(val, "(blob)"))) {
  167. unsigned len = strlen(s);
  168. /* two characters per byte: */
  169. len = DIV_ROUND_UP(len, 2);
  170. *data_size = len;
  171. if (in_size < len)
  172. return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
  173. if (!data)
  174. return EFI_EXIT(EFI_INVALID_PARAMETER);
  175. if (hex2mem(data, s, len * 2))
  176. return EFI_EXIT(EFI_DEVICE_ERROR);
  177. debug("%s: got value: \"%s\"\n", __func__, s);
  178. } else if ((s = prefix(val, "(utf8)"))) {
  179. unsigned len = strlen(s) + 1;
  180. *data_size = len;
  181. if (in_size < len)
  182. return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
  183. if (!data)
  184. return EFI_EXIT(EFI_INVALID_PARAMETER);
  185. memcpy(data, s, len);
  186. ((char *)data)[len] = '\0';
  187. debug("%s: got value: \"%s\"\n", __func__, (char *)data);
  188. } else {
  189. debug("%s: invalid value: '%s'\n", __func__, val);
  190. return EFI_EXIT(EFI_DEVICE_ERROR);
  191. }
  192. if (attributes)
  193. *attributes = attr & EFI_VARIABLE_MASK;
  194. return EFI_EXIT(EFI_SUCCESS);
  195. }
  196. /* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetNextVariableName.28.29 */
  197. efi_status_t EFIAPI efi_get_next_variable(
  198. unsigned long *variable_name_size,
  199. s16 *variable_name, efi_guid_t *vendor)
  200. {
  201. EFI_ENTRY("%p \"%ls\" %pUl", variable_name_size, variable_name, vendor);
  202. return EFI_EXIT(EFI_DEVICE_ERROR);
  203. }
  204. /* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#SetVariable.28.29 */
  205. efi_status_t EFIAPI efi_set_variable(s16 *variable_name,
  206. efi_guid_t *vendor, u32 attributes,
  207. unsigned long data_size, void *data)
  208. {
  209. char native_name[MAX_NATIVE_VAR_NAME + 1];
  210. efi_status_t ret = EFI_SUCCESS;
  211. char *val, *s;
  212. u32 attr;
  213. EFI_ENTRY("\"%ls\" %pUl %x %lu %p", variable_name, vendor, attributes,
  214. data_size, data);
  215. if (!variable_name || !vendor)
  216. return EFI_EXIT(EFI_INVALID_PARAMETER);
  217. ret = efi_to_native(native_name, variable_name, vendor);
  218. if (ret)
  219. return EFI_EXIT(ret);
  220. #define ACCESS_ATTR (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)
  221. if ((data_size == 0) || !(attributes & ACCESS_ATTR)) {
  222. /* delete the variable: */
  223. env_set(native_name, NULL);
  224. return EFI_EXIT(EFI_SUCCESS);
  225. }
  226. val = env_get(native_name);
  227. if (val) {
  228. parse_attr(val, &attr);
  229. if (attr & READ_ONLY)
  230. return EFI_EXIT(EFI_WRITE_PROTECTED);
  231. }
  232. val = malloc(2 * data_size + strlen("{ro,run,boot}(blob)") + 1);
  233. if (!val)
  234. return EFI_EXIT(EFI_OUT_OF_RESOURCES);
  235. s = val;
  236. /* store attributes: */
  237. attributes &= (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS);
  238. s += sprintf(s, "{");
  239. while (attributes) {
  240. u32 attr = 1 << (ffs(attributes) - 1);
  241. if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS)
  242. s += sprintf(s, "boot");
  243. else if (attr == EFI_VARIABLE_RUNTIME_ACCESS)
  244. s += sprintf(s, "run");
  245. attributes &= ~attr;
  246. if (attributes)
  247. s += sprintf(s, ",");
  248. }
  249. s += sprintf(s, "}");
  250. /* store payload: */
  251. s += sprintf(s, "(blob)");
  252. s = mem2hex(s, data, data_size);
  253. *s = '\0';
  254. debug("%s: setting: %s=%s\n", __func__, native_name, val);
  255. if (env_set(native_name, val))
  256. ret = EFI_DEVICE_ERROR;
  257. free(val);
  258. return EFI_EXIT(ret);
  259. }