From nobody Wed Nov 27 08:39:49 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1700061410; cv=none; d=zohomail.com; s=zohoarc; b=Nv2iVuCaLkJLlBzMTrIK8v5v7os4awErWg9ZoOUCTwyYVsdT5AU+Nz2FI6uZoCsq28MBpLg/kpYjiBwvnKIBkgx2d3KufJI9dvDqfT9GkOdRq5CCa9QUWOzYB9Lui0Dt1xt53vby12RquLJBUZW39kYlN9NVLPjlLfNVazSzyHk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1700061410; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=SOhJPPcnT8CXmET/U4fU6CBNjKcfyHQ7CBakNpC48zE=; b=gQvugAhdhHh98LYUkf1NcgCJMpFuiAT5YAypM7W/G9dJtb31OEhnBIDOy2fGKpgBvxVSCXC54ulPK8Zy0ytgMLZ410ukdL3qvP/lMUhV3FM31Y8bqxZxfKd+MMBxa6Kf78b3NxFxNVaxIfBjOXxFKvbA5kPz6FwWPyyn4n2KUc0= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1700061410762769.773344609616; Wed, 15 Nov 2023 07:16:50 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r3HaI-00047q-Nj; Wed, 15 Nov 2023 10:13:43 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3HZl-0003uh-3z for qemu-devel@nongnu.org; Wed, 15 Nov 2023 10:13:12 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3HZe-0003G7-Hh for qemu-devel@nongnu.org; Wed, 15 Nov 2023 10:13:07 -0500 Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-283-bB4xzy9jMemIXWvGXvX61g-1; Wed, 15 Nov 2023 10:12:50 -0500 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.rdu2.redhat.com [10.11.54.7]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 62CDD3C100A8; Wed, 15 Nov 2023 15:12:50 +0000 (UTC) Received: from sirius.home.kraxel.org (unknown [10.39.192.56]) by smtp.corp.redhat.com (Postfix) with ESMTPS id EDE1D1C060AE; Wed, 15 Nov 2023 15:12:49 +0000 (UTC) Received: by sirius.home.kraxel.org (Postfix, from userid 1000) id E99A8180AC13; Wed, 15 Nov 2023 16:12:42 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700061174; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=SOhJPPcnT8CXmET/U4fU6CBNjKcfyHQ7CBakNpC48zE=; b=XR19/nJY+DAEy0QkC49QI5ndc1jukh5w8/QvMhXqj2QLjruGmqjmhGiJiS5XNt0fj+BTr5 XQ8tG1i1fJn6viEYjx2cyrGZKq8kXf9I/KSLHz0a+toWOtQrqVQXFYTyudt8XI063fnGag Zd8BMUK5eZKTnL03ZrrRPyGYobcT3yM= X-MC-Unique: bB4xzy9jMemIXWvGXvX61g-1 From: Gerd Hoffmann To: qemu-devel@nongnu.org Cc: qemu-arm@nongnu.org, Eric Blake , Thomas Huth , Michael Roth , Paolo Bonzini , Peter Maydell , =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , =?UTF-8?q?L=C3=A1szl=C3=B3=20=C3=89rsek?= , =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= , graf@amazon.com, =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Markus Armbruster , Gerd Hoffmann Subject: [PATCH 09/16] hw/uefi: add support for storing persistent variables on disk Date: Wed, 15 Nov 2023 16:12:31 +0100 Message-ID: <20231115151242.184645-10-kraxel@redhat.com> In-Reply-To: <20231115151242.184645-1-kraxel@redhat.com> References: <20231115151242.184645-1-kraxel@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.7 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.129.124; envelope-from=kraxel@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.099, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1700061412838100003 Content-Type: text/plain; charset="utf-8" Define qapi schema for the uefi variable store state. Use it and the generated visitor helper functions to store persistent variables in JSON format on disk. Signed-off-by: Gerd Hoffmann --- hw/uefi/var-service-json.c | 194 +++++++++++++++++++++++++++++++++++++ qapi/meson.build | 1 + qapi/qapi-schema.json | 1 + qapi/uefi.json | 40 ++++++++ 4 files changed, 236 insertions(+) create mode 100644 hw/uefi/var-service-json.c create mode 100644 qapi/uefi.json diff --git a/hw/uefi/var-service-json.c b/hw/uefi/var-service-json.c new file mode 100644 index 000000000000..d8d74945bbf1 --- /dev/null +++ b/hw/uefi/var-service-json.c @@ -0,0 +1,194 @@ +/* + * 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 "sysemu/dma.h" + +#include "hw/uefi/var-service.h" + +#include "qapi/dealloc-visitor.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-output-visitor.h" +#include "qapi/qmp/qobject.h" +#include "qapi/qmp/qjson.h" +#include "qapi/qapi-types-uefi.h" +#include "qapi/qapi-visit-uefi.h" + +static UefiVarStore *uefi_vars_to_qapi(uefi_vars_state *uv) +{ + static const char hex[] =3D { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', + }; + UefiVarStore *vs; + UefiVariableList **tail; + UefiVariable *v; + QemuUUID be; + uefi_variable *var; + uint8_t *data; + unsigned int i; + + vs =3D g_new0(UefiVarStore, 1); + vs->version =3D 2; + tail =3D &vs->variables; + + QTAILQ_FOREACH(var, &uv->variables, next) { + if (!(var->attributes & EFI_VARIABLE_NON_VOLATILE)) { + continue; + } + + v =3D g_new0(UefiVariable, 1); + be =3D qemu_uuid_bswap(var->guid); + v->guid =3D qemu_uuid_unparse_strdup(&be); + v->name =3D uefi_ucs2_to_ascii(var->name, var->name_size); + v->attr =3D var->attributes; + + v->data =3D g_malloc(var->data_size * 2 + 1); + data =3D var->data; + for (i =3D 0; i < var->data_size * 2;) { + v->data[i++] =3D hex[*data >> 4]; + v->data[i++] =3D hex[*data & 15]; + data++; + } + v->data[i++] =3D 0; + + 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 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 =3D vs->variables; item !=3D NULL; item =3D item->next) { + v =3D item->value; + + var =3D g_new0(uefi_variable, 1); + var->attributes =3D v->attr; + qemu_uuid_parse(v->guid, &be); + var->guid =3D qemu_uuid_bswap(be); + + len =3D strlen(v->name); + var->name_size =3D len * 2 + 2; + var->name =3D g_malloc(var->name_size); + for (i =3D 0; i <=3D len; i++) { + var->name[i] =3D v->name[i]; + } + + len =3D strlen(v->data); + var->data_size =3D len / 2; + var->data =3D data =3D g_malloc(var->data_size); + for (i =3D 0; i < len; i +=3D 2) { + *(data++) =3D + parse_hexchar(v->data[i]) << 4 | + parse_hexchar(v->data[i + 1]); + } + + QTAILQ_INSERT_TAIL(&uv->variables, var, next); + } +} + +static GString *uefi_vars_to_json(uefi_vars_state *uv) +{ + UefiVarStore *vs =3D uefi_vars_to_qapi(uv); + QObject *qobj =3D NULL; + Visitor *v; + GString *gstr; + + v =3D 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 =3D 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 =3D qemu_create(uv->jsonfile, O_RDWR, 0666, errp); + } +} + +void uefi_vars_json_save(uefi_vars_state *uv) +{ + GString *gstr; + + if (uv->jsonfd =3D=3D -1) { + return; + } + + gstr =3D uefi_vars_to_json(uv); + + lseek(uv->jsonfd, 0, SEEK_SET); + write(uv->jsonfd, gstr->str, gstr->len); + ftruncate(uv->jsonfd, gstr->len); + fsync(uv->jsonfd); + + g_string_free(gstr, true); +} + +void uefi_vars_json_load(uefi_vars_state *uv, Error **errp) +{ + UefiVarStore *vs; + QObject *qobj; + Visitor *v; + char *str; + size_t len; + + if (uv->jsonfd =3D=3D -1) { + return; + } + + len =3D lseek(uv->jsonfd, 0, SEEK_END); + if (len =3D=3D 0) { + return; + } + + str =3D g_malloc(len + 1); + lseek(uv->jsonfd, 0, SEEK_SET); + read(uv->jsonfd, str, len); + str[len] =3D 0; + + qobj =3D qobject_from_json(str, errp); + v =3D qobject_input_visitor_new(qobj); + visit_type_UefiVarStore(v, NULL, &vs, errp); + visit_free(v); + + if (!(*errp)) { + uefi_vars_from_qapi(uv, vs); + } + + qapi_free_UefiVarStore(vs); + qobject_unref(qobj); + g_free(str); +} diff --git a/qapi/meson.build b/qapi/meson.build index f81a37565ca7..0cd1b1e0b2d1 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -64,6 +64,7 @@ if have_system 'rdma', 'rocker', 'tpm', + 'uefi', ] endif if have_system or have_tools diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index c01ec335e680..d169b4660b20 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -80,3 +80,4 @@ { 'include': 'virtio.json' } { 'include': 'cryptodev.json' } { 'include': 'cxl.json' } +{ 'include': 'uefi.json' } diff --git a/qapi/uefi.json b/qapi/uefi.json new file mode 100644 index 000000000000..152c4d404824 --- /dev/null +++ b/qapi/uefi.json @@ -0,0 +1,40 @@ +# -*- Mode: Python -*- +# vim: filetype=3Dpython +# + +## +# @UefiVariable: +# +# UEFI Variable +# +# @guid: variable namespace guid +# +# @name: variable name (utf-8) +# +# @attr: variable attributes +# +# @data: variable content (base64) +# +# Since: 9.0 +## +{ 'struct' : 'UefiVariable', + 'data' : { 'guid' : 'str', + 'name' : 'str', + 'attr' : 'int', + 'data' : 'str', + '*time' : 'str'}} + +## +# @UefiVarStore: +# +# UEFI Variable Store +# +# @version: 2 +# +# @variables: list of uefi variables +# +# Since: 9.0 +## +{ 'struct' : 'UefiVarStore', + 'data' : { 'version' : 'int', + 'variables' : [ 'UefiVariable' ] }} --=20 2.41.0