Skip to content

Commit 1205894

Browse files
committed
hw/uefi: add var-service-json.c + qapi for NV vars.
Define qapi schema for the uefi variable store state. Use it and the generated visitor helper functions to store persistent (EFI_VARIABLE_NON_VOLATILE) variables in JSON format on disk. Acked-by: Markus Armbruster <[email protected]> Signed-off-by: Gerd Hoffmann <[email protected]> Message-ID: <[email protected]> [ incremental fix squashed in ] Message-ID: <pji24p6oag7cn2rovus7rquo7q2c6tokuquobfro2sqorky7vu@tk7cxud6jw7f>
1 parent f903e88 commit 1205894

File tree

4 files changed

+309
-0
lines changed

4 files changed

+309
-0
lines changed

hw/uefi/var-service-json.c

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
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+
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+
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+
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+
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+
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+
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+
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+
179+
void uefi_vars_json_save(uefi_vars_state *uv)
180+
{
181+
GString *gstr;
182+
int rc;
183+
184+
if (uv->jsonfd == -1) {
185+
return;
186+
}
187+
188+
gstr = uefi_vars_to_json(uv);
189+
190+
lseek(uv->jsonfd, 0, SEEK_SET);
191+
rc = ftruncate(uv->jsonfd, 0);
192+
if (rc != 0) {
193+
warn_report("%s: ftruncate error", __func__);
194+
}
195+
rc = write(uv->jsonfd, gstr->str, gstr->len);
196+
if (rc != gstr->len) {
197+
warn_report("%s: write error", __func__);
198+
}
199+
fsync(uv->jsonfd);
200+
201+
g_string_free(gstr, true);
202+
}
203+
204+
void uefi_vars_json_load(uefi_vars_state *uv, Error **errp)
205+
{
206+
UefiVarStore *vs;
207+
QObject *qobj;
208+
Visitor *v;
209+
char *str;
210+
size_t len;
211+
int rc;
212+
213+
if (uv->jsonfd == -1) {
214+
return;
215+
}
216+
217+
len = lseek(uv->jsonfd, 0, SEEK_END);
218+
if (len == 0) {
219+
return;
220+
}
221+
222+
str = g_malloc(len + 1);
223+
lseek(uv->jsonfd, 0, SEEK_SET);
224+
rc = read(uv->jsonfd, str, len);
225+
if (rc != len) {
226+
warn_report("%s: read error", __func__);
227+
}
228+
str[len] = 0;
229+
230+
qobj = qobject_from_json(str, errp);
231+
v = qobject_input_visitor_new(qobj);
232+
visit_type_UefiVarStore(v, NULL, &vs, errp);
233+
visit_free(v);
234+
235+
if (!(*errp)) {
236+
uefi_vars_from_qapi(uv, vs);
237+
uefi_vars_update_storage(uv);
238+
}
239+
240+
qapi_free_UefiVarStore(vs);
241+
qobject_unref(qobj);
242+
g_free(str);
243+
}

qapi/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ if have_system
6565
'pci',
6666
'rocker',
6767
'tpm',
68+
'uefi',
6869
]
6970
endif
7071
if have_system or have_tools

qapi/qapi-schema.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,4 @@
8181
{ 'include': 'vfio.json' }
8282
{ 'include': 'cryptodev.json' }
8383
{ 'include': 'cxl.json' }
84+
{ 'include': 'uefi.json' }

qapi/uefi.json

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# -*- Mode: Python -*-
2+
# vim: filetype=python
3+
#
4+
5+
##
6+
# = UEFI Variable Store
7+
#
8+
# The qemu efi variable store implementation (hw/uefi/) uses this to
9+
# store non-volatile variables in json format on disk.
10+
#
11+
# This is an existing format already supported by (at least) two other
12+
# projects, specifically https://gitlab.com/kraxel/virt-firmware and
13+
# https://github.com/awslabs/python-uefivars.
14+
##
15+
16+
##
17+
# @UefiVariable:
18+
#
19+
# UEFI Variable. Check the UEFI specifification for more detailed
20+
# information on the fields.
21+
#
22+
# @guid: variable namespace GUID
23+
#
24+
# @name: variable name, in UTF-8 encoding.
25+
#
26+
# @attr: variable attributes.
27+
#
28+
# @data: variable value, encoded as hex string.
29+
#
30+
# @time: variable modification time. EFI_TIME struct, encoded as hex
31+
# string. Used only for authenticated variables, where the
32+
# EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute bit
33+
# is set.
34+
#
35+
# @digest: variable certificate digest. Used to verify the signature
36+
# of updates for authenticated variables. UEFI has two kinds of
37+
# authenticated variables. The secure boot variables ('PK',
38+
# 'KEK', 'db' and 'dbx') have hard coded signature checking rules.
39+
# For other authenticated variables the firmware stores a digest
40+
# of the signing certificate at variable creation time, and any
41+
# updates must be signed with the same certificate.
42+
#
43+
# Since: 10.0
44+
##
45+
{ 'struct' : 'UefiVariable',
46+
'data' : { 'guid' : 'str',
47+
'name' : 'str',
48+
'attr' : 'int',
49+
'data' : 'str',
50+
'*time' : 'str',
51+
'*digest' : 'str'}}
52+
53+
##
54+
# @UefiVarStore:
55+
#
56+
# @version: currently always 2
57+
#
58+
# @variables: list of UEFI variables
59+
#
60+
# Since: 10.0
61+
##
62+
{ 'struct' : 'UefiVarStore',
63+
'data' : { 'version' : 'int',
64+
'variables' : [ 'UefiVariable' ] }}

0 commit comments

Comments
 (0)