var-service-json.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /*
  2. * SPDX-License-Identifier: GPL-2.0-or-later
  3. *
  4. * uefi vars device - serialize non-volatile varstore from/to json,
  5. * using qapi
  6. *
  7. * tools which can read/write these json files:
  8. * - https://gitlab.com/kraxel/virt-firmware
  9. * - https://github.com/awslabs/python-uefivars
  10. */
  11. #include "qemu/osdep.h"
  12. #include "qemu/cutils.h"
  13. #include "qemu/error-report.h"
  14. #include "system/dma.h"
  15. #include "hw/uefi/var-service.h"
  16. #include "qobject/qobject.h"
  17. #include "qobject/qjson.h"
  18. #include "qapi/dealloc-visitor.h"
  19. #include "qapi/qobject-input-visitor.h"
  20. #include "qapi/qobject-output-visitor.h"
  21. #include "qapi/qapi-types-uefi.h"
  22. #include "qapi/qapi-visit-uefi.h"
  23. static char *generate_hexstr(void *data, size_t len)
  24. {
  25. static const char hex[] = {
  26. '0', '1', '2', '3', '4', '5', '6', '7',
  27. '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
  28. };
  29. uint8_t *src = data;
  30. char *dest;
  31. size_t i;
  32. dest = g_malloc(len * 2 + 1);
  33. for (i = 0; i < len * 2;) {
  34. dest[i++] = hex[*src >> 4];
  35. dest[i++] = hex[*src & 15];
  36. src++;
  37. }
  38. dest[i++] = 0;
  39. return dest;
  40. }
  41. static UefiVarStore *uefi_vars_to_qapi(uefi_vars_state *uv)
  42. {
  43. UefiVarStore *vs;
  44. UefiVariableList **tail;
  45. UefiVariable *v;
  46. QemuUUID be;
  47. uefi_variable *var;
  48. vs = g_new0(UefiVarStore, 1);
  49. vs->version = 2;
  50. tail = &vs->variables;
  51. QTAILQ_FOREACH(var, &uv->variables, next) {
  52. if (!(var->attributes & EFI_VARIABLE_NON_VOLATILE)) {
  53. continue;
  54. }
  55. v = g_new0(UefiVariable, 1);
  56. be = qemu_uuid_bswap(var->guid);
  57. v->guid = qemu_uuid_unparse_strdup(&be);
  58. v->name = uefi_ucs2_to_ascii(var->name, var->name_size);
  59. v->attr = var->attributes;
  60. v->data = generate_hexstr(var->data, var->data_size);
  61. if (var->attributes &
  62. EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) {
  63. v->time = generate_hexstr(&var->time, sizeof(var->time));
  64. if (var->digest && var->digest_size) {
  65. v->digest = generate_hexstr(var->digest, var->digest_size);
  66. }
  67. }
  68. QAPI_LIST_APPEND(tail, v);
  69. }
  70. return vs;
  71. }
  72. static unsigned parse_hexchar(char c)
  73. {
  74. switch (c) {
  75. case '0' ... '9': return c - '0';
  76. case 'a' ... 'f': return c - 'a' + 0xa;
  77. case 'A' ... 'F': return c - 'A' + 0xA;
  78. default: return 0;
  79. }
  80. }
  81. static void parse_hexstr(void *dest, char *src, int len)
  82. {
  83. uint8_t *data = dest;
  84. size_t i;
  85. for (i = 0; i < len; i += 2) {
  86. *(data++) =
  87. parse_hexchar(src[i]) << 4 |
  88. parse_hexchar(src[i + 1]);
  89. }
  90. }
  91. static void uefi_vars_from_qapi(uefi_vars_state *uv, UefiVarStore *vs)
  92. {
  93. UefiVariableList *item;
  94. UefiVariable *v;
  95. QemuUUID be;
  96. uefi_variable *var;
  97. uint8_t *data;
  98. size_t i, len;
  99. for (item = vs->variables; item != NULL; item = item->next) {
  100. v = item->value;
  101. var = g_new0(uefi_variable, 1);
  102. var->attributes = v->attr;
  103. qemu_uuid_parse(v->guid, &be);
  104. var->guid = qemu_uuid_bswap(be);
  105. len = strlen(v->name);
  106. var->name_size = len * 2 + 2;
  107. var->name = g_malloc(var->name_size);
  108. for (i = 0; i <= len; i++) {
  109. var->name[i] = v->name[i];
  110. }
  111. len = strlen(v->data);
  112. var->data_size = len / 2;
  113. var->data = data = g_malloc(var->data_size);
  114. parse_hexstr(var->data, v->data, len);
  115. if (v->time && strlen(v->time) == 32) {
  116. parse_hexstr(&var->time, v->time, 32);
  117. }
  118. if (v->digest) {
  119. len = strlen(v->digest);
  120. var->digest_size = len / 2;
  121. var->digest = g_malloc(var->digest_size);
  122. parse_hexstr(var->digest, v->digest, len);
  123. }
  124. QTAILQ_INSERT_TAIL(&uv->variables, var, next);
  125. }
  126. }
  127. static GString *uefi_vars_to_json(uefi_vars_state *uv)
  128. {
  129. UefiVarStore *vs = uefi_vars_to_qapi(uv);
  130. QObject *qobj = NULL;
  131. Visitor *v;
  132. GString *gstr;
  133. v = qobject_output_visitor_new(&qobj);
  134. if (visit_type_UefiVarStore(v, NULL, &vs, NULL)) {
  135. visit_complete(v, &qobj);
  136. }
  137. visit_free(v);
  138. qapi_free_UefiVarStore(vs);
  139. gstr = qobject_to_json_pretty(qobj, true);
  140. qobject_unref(qobj);
  141. return gstr;
  142. }
  143. void uefi_vars_json_init(uefi_vars_state *uv, Error **errp)
  144. {
  145. if (uv->jsonfile) {
  146. uv->jsonfd = qemu_create(uv->jsonfile, O_RDWR, 0666, errp);
  147. }
  148. }
  149. void uefi_vars_json_save(uefi_vars_state *uv)
  150. {
  151. g_autoptr(GString) gstr = NULL;
  152. int rc;
  153. if (uv->jsonfd == -1) {
  154. return;
  155. }
  156. gstr = uefi_vars_to_json(uv);
  157. rc = lseek(uv->jsonfd, 0, SEEK_SET);
  158. if (rc < 0) {
  159. warn_report("%s: lseek error", __func__);
  160. return;
  161. }
  162. rc = ftruncate(uv->jsonfd, 0);
  163. if (rc != 0) {
  164. warn_report("%s: ftruncate error", __func__);
  165. return;
  166. }
  167. rc = write(uv->jsonfd, gstr->str, gstr->len);
  168. if (rc != gstr->len) {
  169. warn_report("%s: write error", __func__);
  170. return;
  171. }
  172. fsync(uv->jsonfd);
  173. }
  174. void uefi_vars_json_load(uefi_vars_state *uv, Error **errp)
  175. {
  176. UefiVarStore *vs;
  177. QObject *qobj;
  178. Visitor *v;
  179. char *str;
  180. ssize_t len;
  181. int rc;
  182. if (uv->jsonfd == -1) {
  183. return;
  184. }
  185. len = lseek(uv->jsonfd, 0, SEEK_END);
  186. if (len < 0) {
  187. warn_report("%s: lseek error", __func__);
  188. return;
  189. }
  190. if (len == 0) {
  191. /* empty file */
  192. return;
  193. }
  194. str = g_malloc(len + 1);
  195. lseek(uv->jsonfd, 0, SEEK_SET);
  196. rc = read(uv->jsonfd, str, len);
  197. if (rc != len) {
  198. warn_report("%s: read error", __func__);
  199. g_free(str);
  200. return;
  201. }
  202. str[len] = 0;
  203. qobj = qobject_from_json(str, errp);
  204. v = qobject_input_visitor_new(qobj);
  205. visit_type_UefiVarStore(v, NULL, &vs, errp);
  206. visit_free(v);
  207. if (!(*errp)) {
  208. uefi_vars_from_qapi(uv, vs);
  209. uefi_vars_update_storage(uv);
  210. }
  211. qapi_free_UefiVarStore(vs);
  212. qobject_unref(qobj);
  213. g_free(str);
  214. }