efi_variable.c 7.0 KB

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