123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- /*
- * SPDX-License-Identifier: GPL-2.0-or-later
- *
- * uefi vars device - serialize non-volatile varstore from/to json,
- * using qapi
- *
- * tools which can read/write these json files:
- * - https://gitlab.com/kraxel/virt-firmware
- * - https://github.com/awslabs/python-uefivars
- */
- #include "qemu/osdep.h"
- #include "qemu/cutils.h"
- #include "qemu/error-report.h"
- #include "system/dma.h"
- #include "hw/uefi/var-service.h"
- #include "qobject/qobject.h"
- #include "qobject/qjson.h"
- #include "qapi/dealloc-visitor.h"
- #include "qapi/qobject-input-visitor.h"
- #include "qapi/qobject-output-visitor.h"
- #include "qapi/qapi-types-uefi.h"
- #include "qapi/qapi-visit-uefi.h"
- static char *generate_hexstr(void *data, size_t len)
- {
- static const char hex[] = {
- '0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
- };
- uint8_t *src = data;
- char *dest;
- size_t i;
- dest = g_malloc(len * 2 + 1);
- for (i = 0; i < len * 2;) {
- dest[i++] = hex[*src >> 4];
- dest[i++] = hex[*src & 15];
- src++;
- }
- dest[i++] = 0;
- return dest;
- }
- static UefiVarStore *uefi_vars_to_qapi(uefi_vars_state *uv)
- {
- UefiVarStore *vs;
- UefiVariableList **tail;
- UefiVariable *v;
- QemuUUID be;
- uefi_variable *var;
- vs = g_new0(UefiVarStore, 1);
- vs->version = 2;
- tail = &vs->variables;
- QTAILQ_FOREACH(var, &uv->variables, next) {
- if (!(var->attributes & EFI_VARIABLE_NON_VOLATILE)) {
- continue;
- }
- v = g_new0(UefiVariable, 1);
- be = qemu_uuid_bswap(var->guid);
- v->guid = qemu_uuid_unparse_strdup(&be);
- v->name = uefi_ucs2_to_ascii(var->name, var->name_size);
- v->attr = var->attributes;
- v->data = generate_hexstr(var->data, var->data_size);
- if (var->attributes &
- EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) {
- v->time = generate_hexstr(&var->time, sizeof(var->time));
- if (var->digest && var->digest_size) {
- v->digest = generate_hexstr(var->digest, var->digest_size);
- }
- }
- QAPI_LIST_APPEND(tail, v);
- }
- return vs;
- }
- static unsigned parse_hexchar(char c)
- {
- switch (c) {
- case '0' ... '9': return c - '0';
- case 'a' ... 'f': return c - 'a' + 0xa;
- case 'A' ... 'F': return c - 'A' + 0xA;
- default: return 0;
- }
- }
- static void parse_hexstr(void *dest, char *src, int len)
- {
- uint8_t *data = dest;
- size_t i;
- for (i = 0; i < len; i += 2) {
- *(data++) =
- parse_hexchar(src[i]) << 4 |
- parse_hexchar(src[i + 1]);
- }
- }
- static void uefi_vars_from_qapi(uefi_vars_state *uv, UefiVarStore *vs)
- {
- UefiVariableList *item;
- UefiVariable *v;
- QemuUUID be;
- uefi_variable *var;
- uint8_t *data;
- size_t i, len;
- for (item = vs->variables; item != NULL; item = item->next) {
- v = item->value;
- var = g_new0(uefi_variable, 1);
- var->attributes = v->attr;
- qemu_uuid_parse(v->guid, &be);
- var->guid = qemu_uuid_bswap(be);
- len = strlen(v->name);
- var->name_size = len * 2 + 2;
- var->name = g_malloc(var->name_size);
- for (i = 0; i <= len; i++) {
- var->name[i] = v->name[i];
- }
- len = strlen(v->data);
- var->data_size = len / 2;
- var->data = data = g_malloc(var->data_size);
- parse_hexstr(var->data, v->data, len);
- if (v->time && strlen(v->time) == 32) {
- parse_hexstr(&var->time, v->time, 32);
- }
- if (v->digest) {
- len = strlen(v->digest);
- var->digest_size = len / 2;
- var->digest = g_malloc(var->digest_size);
- parse_hexstr(var->digest, v->digest, len);
- }
- QTAILQ_INSERT_TAIL(&uv->variables, var, next);
- }
- }
- static GString *uefi_vars_to_json(uefi_vars_state *uv)
- {
- UefiVarStore *vs = uefi_vars_to_qapi(uv);
- QObject *qobj = NULL;
- Visitor *v;
- GString *gstr;
- v = qobject_output_visitor_new(&qobj);
- if (visit_type_UefiVarStore(v, NULL, &vs, NULL)) {
- visit_complete(v, &qobj);
- }
- visit_free(v);
- qapi_free_UefiVarStore(vs);
- gstr = qobject_to_json_pretty(qobj, true);
- qobject_unref(qobj);
- return gstr;
- }
- void uefi_vars_json_init(uefi_vars_state *uv, Error **errp)
- {
- if (uv->jsonfile) {
- uv->jsonfd = qemu_create(uv->jsonfile, O_RDWR, 0666, errp);
- }
- }
- void uefi_vars_json_save(uefi_vars_state *uv)
- {
- g_autoptr(GString) gstr = NULL;
- int rc;
- if (uv->jsonfd == -1) {
- return;
- }
- gstr = uefi_vars_to_json(uv);
- rc = lseek(uv->jsonfd, 0, SEEK_SET);
- if (rc < 0) {
- warn_report("%s: lseek error", __func__);
- return;
- }
- rc = ftruncate(uv->jsonfd, 0);
- if (rc != 0) {
- warn_report("%s: ftruncate error", __func__);
- return;
- }
- rc = write(uv->jsonfd, gstr->str, gstr->len);
- if (rc != gstr->len) {
- warn_report("%s: write error", __func__);
- return;
- }
- fsync(uv->jsonfd);
- }
- void uefi_vars_json_load(uefi_vars_state *uv, Error **errp)
- {
- UefiVarStore *vs;
- QObject *qobj;
- Visitor *v;
- char *str;
- ssize_t len;
- int rc;
- if (uv->jsonfd == -1) {
- return;
- }
- len = lseek(uv->jsonfd, 0, SEEK_END);
- if (len < 0) {
- warn_report("%s: lseek error", __func__);
- return;
- }
- if (len == 0) {
- /* empty file */
- return;
- }
- str = g_malloc(len + 1);
- lseek(uv->jsonfd, 0, SEEK_SET);
- rc = read(uv->jsonfd, str, len);
- if (rc != len) {
- warn_report("%s: read error", __func__);
- g_free(str);
- return;
- }
- str[len] = 0;
- qobj = qobject_from_json(str, errp);
- v = qobject_input_visitor_new(qobj);
- visit_type_UefiVarStore(v, NULL, &vs, errp);
- visit_free(v);
- if (!(*errp)) {
- uefi_vars_from_qapi(uv, vs);
- uefi_vars_update_storage(uv);
- }
- qapi_free_UefiVarStore(vs);
- qobject_unref(qobj);
- g_free(str);
- }
|