efi_var_mem.c 7.8 KB

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