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