efi_variable.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  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 PREFIX_LEN (strlen("efi_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx_"))
  44. static int hex(int ch)
  45. {
  46. if (ch >= 'a' && ch <= 'f')
  47. return ch-'a'+10;
  48. if (ch >= '0' && ch <= '9')
  49. return ch-'0';
  50. if (ch >= 'A' && ch <= 'F')
  51. return ch-'A'+10;
  52. return -1;
  53. }
  54. static int hex2mem(u8 *mem, const char *hexstr, int size)
  55. {
  56. int nibble;
  57. int i;
  58. for (i = 0; i < size; i++) {
  59. if (*hexstr == '\0')
  60. break;
  61. nibble = hex(*hexstr);
  62. if (nibble < 0)
  63. return -1;
  64. *mem = nibble;
  65. hexstr++;
  66. nibble = hex(*hexstr);
  67. if (nibble < 0)
  68. return -1;
  69. *mem = (*mem << 4) | nibble;
  70. hexstr++;
  71. mem++;
  72. }
  73. return i;
  74. }
  75. static char *mem2hex(char *hexstr, const u8 *mem, int count)
  76. {
  77. static const char hexchars[] = "0123456789abcdef";
  78. while (count-- > 0) {
  79. u8 ch = *mem++;
  80. *hexstr++ = hexchars[ch >> 4];
  81. *hexstr++ = hexchars[ch & 0xf];
  82. }
  83. return hexstr;
  84. }
  85. static efi_status_t efi_to_native(char **native, const u16 *variable_name,
  86. efi_guid_t *vendor)
  87. {
  88. size_t len;
  89. char *pos;
  90. len = PREFIX_LEN + utf16_utf8_strlen(variable_name) + 1;
  91. *native = malloc(len);
  92. if (!*native)
  93. return EFI_OUT_OF_RESOURCES;
  94. pos = *native;
  95. pos += sprintf(pos, "efi_%pUl_", vendor);
  96. utf16_utf8_strcpy(&pos, variable_name);
  97. return EFI_SUCCESS;
  98. }
  99. static const char *prefix(const char *str, const char *prefix)
  100. {
  101. size_t n = strlen(prefix);
  102. if (!strncmp(prefix, str, n))
  103. return str + n;
  104. return NULL;
  105. }
  106. /* parse attributes part of variable value, if present: */
  107. static const char *parse_attr(const char *str, u32 *attrp)
  108. {
  109. u32 attr = 0;
  110. char sep = '{';
  111. if (*str != '{') {
  112. *attrp = EFI_VARIABLE_BOOTSERVICE_ACCESS;
  113. return str;
  114. }
  115. while (*str == sep) {
  116. const char *s;
  117. str++;
  118. if ((s = prefix(str, "ro"))) {
  119. attr |= READ_ONLY;
  120. } else if ((s = prefix(str, "boot"))) {
  121. attr |= EFI_VARIABLE_BOOTSERVICE_ACCESS;
  122. } else if ((s = prefix(str, "run"))) {
  123. attr |= EFI_VARIABLE_RUNTIME_ACCESS;
  124. } else {
  125. printf("invalid attribute: %s\n", str);
  126. break;
  127. }
  128. str = s;
  129. sep = ',';
  130. }
  131. str++;
  132. *attrp = attr;
  133. return str;
  134. }
  135. /* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetVariable.28.29 */
  136. efi_status_t EFIAPI efi_get_variable(u16 *variable_name, efi_guid_t *vendor,
  137. u32 *attributes, efi_uintn_t *data_size,
  138. void *data)
  139. {
  140. char *native_name;
  141. efi_status_t ret;
  142. unsigned long in_size;
  143. const char *val, *s;
  144. u32 attr;
  145. EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes,
  146. data_size, data);
  147. if (!variable_name || !vendor || !data_size)
  148. return EFI_EXIT(EFI_INVALID_PARAMETER);
  149. ret = efi_to_native(&native_name, variable_name, vendor);
  150. if (ret)
  151. return EFI_EXIT(ret);
  152. debug("%s: get '%s'\n", __func__, native_name);
  153. val = env_get(native_name);
  154. free(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 = NULL, *val = NULL, *s;
  206. efi_status_t ret = EFI_SUCCESS;
  207. u32 attr;
  208. EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes,
  209. data_size, data);
  210. if (!variable_name || !vendor) {
  211. ret = EFI_INVALID_PARAMETER;
  212. goto out;
  213. }
  214. ret = efi_to_native(&native_name, variable_name, vendor);
  215. if (ret)
  216. goto out;
  217. #define ACCESS_ATTR (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)
  218. if ((data_size == 0) || !(attributes & ACCESS_ATTR)) {
  219. /* delete the variable: */
  220. env_set(native_name, NULL);
  221. ret = EFI_SUCCESS;
  222. goto out;
  223. }
  224. val = env_get(native_name);
  225. if (val) {
  226. parse_attr(val, &attr);
  227. if (attr & READ_ONLY) {
  228. /* We should not free val */
  229. val = NULL;
  230. ret = EFI_WRITE_PROTECTED;
  231. goto out;
  232. }
  233. }
  234. val = malloc(2 * data_size + strlen("{ro,run,boot}(blob)") + 1);
  235. if (!val) {
  236. ret = EFI_OUT_OF_RESOURCES;
  237. goto out;
  238. }
  239. s = val;
  240. /* store attributes: */
  241. attributes &= (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS);
  242. s += sprintf(s, "{");
  243. while (attributes) {
  244. u32 attr = 1 << (ffs(attributes) - 1);
  245. if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS)
  246. s += sprintf(s, "boot");
  247. else if (attr == EFI_VARIABLE_RUNTIME_ACCESS)
  248. s += sprintf(s, "run");
  249. attributes &= ~attr;
  250. if (attributes)
  251. s += sprintf(s, ",");
  252. }
  253. s += sprintf(s, "}");
  254. /* store payload: */
  255. s += sprintf(s, "(blob)");
  256. s = mem2hex(s, data, data_size);
  257. *s = '\0';
  258. debug("%s: setting: %s=%s\n", __func__, native_name, val);
  259. if (env_set(native_name, val))
  260. ret = EFI_DEVICE_ERROR;
  261. out:
  262. free(native_name);
  263. free(val);
  264. return EFI_EXIT(ret);
  265. }