efi_var_mem.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * File interface for UEFI variables
  4. *
  5. * Copyright (c) 2020, Heinrich Schuchardt
  6. */
  7. #include <common.h>
  8. #include <efi_loader.h>
  9. #include <efi_variable.h>
  10. #include <u-boot/crc.h>
  11. /*
  12. * The variables efi_var_file and efi_var_entry must be static to avoid
  13. * referencing them via the global offset table (section .got). The GOT
  14. * is neither mapped as EfiRuntimeServicesData nor do we support its
  15. * relocation during SetVirtualAddressMap().
  16. */
  17. static struct efi_var_file __efi_runtime_data *efi_var_buf;
  18. static struct efi_var_entry __efi_runtime_data *efi_current_var;
  19. /**
  20. * efi_var_mem_compare() - compare GUID and name with a variable
  21. *
  22. * @var: variable to compare
  23. * @guid: GUID to compare
  24. * @name: variable name to compare
  25. * @next: pointer to next variable
  26. * Return: true if match
  27. */
  28. static bool __efi_runtime
  29. efi_var_mem_compare(struct efi_var_entry *var, const efi_guid_t *guid,
  30. const u16 *name, struct efi_var_entry **next)
  31. {
  32. int i;
  33. u8 *guid1, *guid2;
  34. const u16 *data, *var_name;
  35. bool match = true;
  36. for (guid1 = (u8 *)&var->guid, guid2 = (u8 *)guid, i = 0;
  37. i < sizeof(efi_guid_t) && match; ++i)
  38. match = (guid1[i] == guid2[i]);
  39. for (data = var->name, var_name = name;; ++data) {
  40. if (match)
  41. match = (*data == *var_name);
  42. if (!*data)
  43. break;
  44. if (*var_name)
  45. ++var_name;
  46. }
  47. ++data;
  48. if (next)
  49. *next = (struct efi_var_entry *)
  50. ALIGN((uintptr_t)data + var->length, 8);
  51. if (match)
  52. efi_current_var = var;
  53. return match;
  54. }
  55. struct efi_var_entry __efi_runtime
  56. *efi_var_mem_find(const efi_guid_t *guid, const u16 *name,
  57. struct efi_var_entry **next)
  58. {
  59. struct efi_var_entry *var, *last;
  60. last = (struct efi_var_entry *)
  61. ((uintptr_t)efi_var_buf + efi_var_buf->length);
  62. if (!*name) {
  63. if (next) {
  64. *next = efi_var_buf->var;
  65. if (*next >= last)
  66. *next = NULL;
  67. }
  68. return NULL;
  69. }
  70. if (efi_current_var &&
  71. efi_var_mem_compare(efi_current_var, guid, name, next)) {
  72. if (next && *next >= last)
  73. *next = NULL;
  74. return efi_current_var;
  75. }
  76. var = efi_var_buf->var;
  77. if (var < last) {
  78. for (; var;) {
  79. struct efi_var_entry *pos;
  80. bool match;
  81. match = efi_var_mem_compare(var, guid, name, &pos);
  82. if (pos >= last)
  83. pos = NULL;
  84. if (match) {
  85. if (next)
  86. *next = pos;
  87. return var;
  88. }
  89. var = pos;
  90. }
  91. }
  92. if (next)
  93. *next = NULL;
  94. return NULL;
  95. }
  96. void __efi_runtime efi_var_mem_del(struct efi_var_entry *var)
  97. {
  98. u16 *data;
  99. struct efi_var_entry *next, *last;
  100. if (!var)
  101. return;
  102. last = (struct efi_var_entry *)
  103. ((uintptr_t)efi_var_buf + efi_var_buf->length);
  104. if (var <= efi_current_var)
  105. efi_current_var = NULL;
  106. for (data = var->name; *data; ++data)
  107. ;
  108. ++data;
  109. next = (struct efi_var_entry *)
  110. ALIGN((uintptr_t)data + var->length, 8);
  111. efi_var_buf->length -= (uintptr_t)next - (uintptr_t)var;
  112. /* efi_memcpy_runtime() can be used because next >= var. */
  113. efi_memcpy_runtime(var, next, (uintptr_t)last - (uintptr_t)next);
  114. efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
  115. efi_var_buf->length -
  116. sizeof(struct efi_var_file));
  117. }
  118. efi_status_t __efi_runtime efi_var_mem_ins(
  119. const u16 *variable_name,
  120. const efi_guid_t *vendor, u32 attributes,
  121. const efi_uintn_t size1, const void *data1,
  122. const efi_uintn_t size2, const void *data2,
  123. const u64 time)
  124. {
  125. u16 *data;
  126. struct efi_var_entry *var;
  127. u32 var_name_len;
  128. var = (struct efi_var_entry *)
  129. ((uintptr_t)efi_var_buf + efi_var_buf->length);
  130. var_name_len = u16_strlen(variable_name) + 1;
  131. data = var->name + var_name_len;
  132. if ((uintptr_t)data - (uintptr_t)efi_var_buf + size1 + size2 >
  133. EFI_VAR_BUF_SIZE)
  134. return EFI_OUT_OF_RESOURCES;
  135. var->attr = attributes;
  136. var->length = size1 + size2;
  137. var->time = time;
  138. efi_memcpy_runtime(&var->guid, vendor, sizeof(efi_guid_t));
  139. efi_memcpy_runtime(var->name, variable_name,
  140. sizeof(u16) * var_name_len);
  141. efi_memcpy_runtime(data, data1, size1);
  142. efi_memcpy_runtime((u8 *)data + size1, data2, size2);
  143. var = (struct efi_var_entry *)
  144. ALIGN((uintptr_t)data + var->length, 8);
  145. efi_var_buf->length = (uintptr_t)var - (uintptr_t)efi_var_buf;
  146. efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
  147. efi_var_buf->length -
  148. sizeof(struct efi_var_file));
  149. return EFI_SUCCESS;
  150. }
  151. u64 __efi_runtime efi_var_mem_free(void)
  152. {
  153. if (efi_var_buf->length + sizeof(struct efi_var_entry) >=
  154. EFI_VAR_BUF_SIZE)
  155. return 0;
  156. return EFI_VAR_BUF_SIZE - efi_var_buf->length -
  157. sizeof(struct efi_var_entry);
  158. }
  159. /**
  160. * efi_var_mem_bs_del() - delete boot service only variables
  161. */
  162. static void efi_var_mem_bs_del(void)
  163. {
  164. struct efi_var_entry *var = efi_var_buf->var;
  165. for (;;) {
  166. struct efi_var_entry *last;
  167. last = (struct efi_var_entry *)
  168. ((uintptr_t)efi_var_buf + efi_var_buf->length);
  169. if (var >= last)
  170. break;
  171. if (var->attr & EFI_VARIABLE_RUNTIME_ACCESS) {
  172. u16 *data;
  173. /* skip variable */
  174. for (data = var->name; *data; ++data)
  175. ;
  176. ++data;
  177. var = (struct efi_var_entry *)
  178. ALIGN((uintptr_t)data + var->length, 8);
  179. } else {
  180. /* delete variable */
  181. efi_var_mem_del(var);
  182. }
  183. }
  184. }
  185. /**
  186. * efi_var_mem_notify_exit_boot_services() - ExitBootService callback
  187. *
  188. * @event: callback event
  189. * @context: callback context
  190. */
  191. static void EFIAPI
  192. efi_var_mem_notify_exit_boot_services(struct efi_event *event, void *context)
  193. {
  194. EFI_ENTRY("%p, %p", event, context);
  195. /* Delete boot service only variables */
  196. efi_var_mem_bs_del();
  197. EFI_EXIT(EFI_SUCCESS);
  198. }
  199. /**
  200. * efi_var_mem_notify_exit_boot_services() - SetVirtualMemoryMap callback
  201. *
  202. * @event: callback event
  203. * @context: callback context
  204. */
  205. static void EFIAPI __efi_runtime
  206. efi_var_mem_notify_virtual_address_map(struct efi_event *event, void *context)
  207. {
  208. efi_convert_pointer(0, (void **)&efi_var_buf);
  209. efi_current_var = NULL;
  210. }
  211. efi_status_t efi_var_mem_init(void)
  212. {
  213. u64 memory;
  214. efi_status_t ret;
  215. struct efi_event *event;
  216. ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
  217. EFI_RUNTIME_SERVICES_DATA,
  218. efi_size_in_pages(EFI_VAR_BUF_SIZE),
  219. &memory);
  220. if (ret != EFI_SUCCESS)
  221. return ret;
  222. efi_var_buf = (struct efi_var_file *)(uintptr_t)memory;
  223. memset(efi_var_buf, 0, EFI_VAR_BUF_SIZE);
  224. efi_var_buf->magic = EFI_VAR_FILE_MAGIC;
  225. efi_var_buf->length = (uintptr_t)efi_var_buf->var -
  226. (uintptr_t)efi_var_buf;
  227. /* crc32 for 0 bytes = 0 */
  228. ret = efi_create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
  229. efi_var_mem_notify_exit_boot_services, NULL,
  230. NULL, &event);
  231. if (ret != EFI_SUCCESS)
  232. return ret;
  233. ret = efi_create_event(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE, TPL_CALLBACK,
  234. efi_var_mem_notify_virtual_address_map, NULL,
  235. NULL, &event);
  236. if (ret != EFI_SUCCESS)
  237. return ret;
  238. return ret;
  239. }
  240. efi_status_t __efi_runtime
  241. efi_get_variable_mem(const u16 *variable_name, const efi_guid_t *vendor,
  242. u32 *attributes, efi_uintn_t *data_size, void *data,
  243. u64 *timep)
  244. {
  245. efi_uintn_t old_size;
  246. struct efi_var_entry *var;
  247. u16 *pdata;
  248. if (!variable_name || !vendor || !data_size)
  249. return EFI_INVALID_PARAMETER;
  250. var = efi_var_mem_find(vendor, variable_name, NULL);
  251. if (!var)
  252. return EFI_NOT_FOUND;
  253. if (attributes)
  254. *attributes = var->attr;
  255. if (timep)
  256. *timep = var->time;
  257. old_size = *data_size;
  258. *data_size = var->length;
  259. if (old_size < var->length)
  260. return EFI_BUFFER_TOO_SMALL;
  261. if (!data)
  262. return EFI_INVALID_PARAMETER;
  263. for (pdata = var->name; *pdata; ++pdata)
  264. ;
  265. ++pdata;
  266. efi_memcpy_runtime(data, pdata, var->length);
  267. return EFI_SUCCESS;
  268. }
  269. efi_status_t __efi_runtime
  270. efi_get_next_variable_name_mem(efi_uintn_t *variable_name_size,
  271. u16 *variable_name, efi_guid_t *vendor)
  272. {
  273. struct efi_var_entry *var;
  274. efi_uintn_t len, old_size;
  275. u16 *pdata;
  276. if (!variable_name_size || !variable_name || !vendor)
  277. return EFI_INVALID_PARAMETER;
  278. len = *variable_name_size >> 1;
  279. if (u16_strnlen(variable_name, len) == len)
  280. return EFI_INVALID_PARAMETER;
  281. if (!efi_var_mem_find(vendor, variable_name, &var) && *variable_name)
  282. return EFI_INVALID_PARAMETER;
  283. if (!var)
  284. return EFI_NOT_FOUND;
  285. for (pdata = var->name; *pdata; ++pdata)
  286. ;
  287. ++pdata;
  288. old_size = *variable_name_size;
  289. *variable_name_size = (uintptr_t)pdata - (uintptr_t)var->name;
  290. if (old_size < *variable_name_size)
  291. return EFI_BUFFER_TOO_SMALL;
  292. efi_memcpy_runtime(variable_name, var->name, *variable_name_size);
  293. efi_memcpy_runtime(vendor, &var->guid, sizeof(efi_guid_t));
  294. return EFI_SUCCESS;
  295. }
  296. void efi_var_buf_update(struct efi_var_file *var_buf)
  297. {
  298. memcpy(efi_var_buf, var_buf, EFI_VAR_BUF_SIZE);
  299. }