From nobody Fri Apr 4 21:22:38 2025 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=1739949734; cv=none; d=zohomail.com; s=zohoarc; b=eT3dKQ3Qr5CzxOgl+HvF6fmo8zbH3gCNZc7F16K3RgyVZ0IYeGruy59ZlOV6+B2szumXvX7mVvCIwdZnj28qDRYUguHgzgz1WzHEgVc/jjhviRLFjLI+tx2c9fk6kxeNDKU3eiI6WBbf4vF0VEZVE5vjP6qGUSYaZfKygqoamJs= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1739949734; 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=ub0g4x4V9SHheKAbxAvxNnt6py7VbQX4lNtEfUlDiNU=; b=EFCJltKdc/5JHzQYZENGoRfo8Z5jaWiTMGF6uDCUahw5jdLj+cGBZiWV94XOdhoJi1aKj4/3CEWePuuhXKbggG4eBS8mW//vpaY+8bm3mGLmG7Zxl11hlhkMqZWDZXQoRjWlGJkLPRGj13wNqOyWYiCPR2GUfBlB+wMzRDJ9fUQ= 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 1739949734196766.6091937261223; Tue, 18 Feb 2025 23:22:14 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tkeLu-00088D-Pd; Wed, 19 Feb 2025 02:18:39 -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 1tkeIr-0002bX-2Z for qemu-devel@nongnu.org; Wed, 19 Feb 2025 02:15:32 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tkeIm-0005Id-Rd for qemu-devel@nongnu.org; Wed, 19 Feb 2025 02:15:28 -0500 Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-458-spqVHIOePQaLlDGytro_6A-1; Wed, 19 Feb 2025 02:15:03 -0500 Received: from mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.40]) (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 mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id D671C1800980; Wed, 19 Feb 2025 07:15:01 +0000 (UTC) Received: from sirius.home.kraxel.org (unknown [10.44.32.78]) by mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 46006194129F; Wed, 19 Feb 2025 07:15:00 +0000 (UTC) Received: by sirius.home.kraxel.org (Postfix, from userid 1000) id E34401801AB5; Wed, 19 Feb 2025 08:14:32 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1739949307; 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=ub0g4x4V9SHheKAbxAvxNnt6py7VbQX4lNtEfUlDiNU=; b=CNZlagonFIxlNboooYvkfofrmJs3ohm6aGG065+99+6D9ZwLiD8azMWcu3XVWSh+dQjp2q oA+jNZrAiqLpuU+AHD+2y4ZJIec6repNe5gQjRca3dO8eZhGRG18gOn+AbBZBLF9XDuZTN VgMU3t8keRnFtmbqGDfmueX8Q8zAKjk= X-MC-Unique: spqVHIOePQaLlDGytro_6A-1 X-Mimecast-MFC-AGG-ID: spqVHIOePQaLlDGytro_6A_1739949302 From: Gerd Hoffmann To: qemu-devel@nongnu.org Cc: qemu-arm@nongnu.org, Ard Biesheuvel , Michael Roth , "Michael S. Tsirkin" , Peter Maydell , Thomas Huth , Eduardo Habkost , Paolo Bonzini , Gerd Hoffmann , Richard Henderson , Eric Blake , =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= , Marcel Apfelbaum , =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , graf@amazon.com, Markus Armbruster Subject: [PATCH v4 14/24] hw/uefi: add var-service-json.c + qapi for NV vars. Date: Wed, 19 Feb 2025 08:14:16 +0100 Message-ID: <20250219071431.50626-15-kraxel@redhat.com> In-Reply-To: <20250219071431.50626-1-kraxel@redhat.com> References: <20250219071431.50626-1-kraxel@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.40 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.133.124; envelope-from=kraxel@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -24 X-Spam_score: -2.5 X-Spam_bar: -- X-Spam_report: (-2.5 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.423, 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_H2=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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: 1739949735290019000 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 (EFI_VARIABLE_NON_VOLATILE) variables in JSON format on disk. Signed-off-by: Gerd Hoffmann --- hw/uefi/var-service-json.c | 243 +++++++++++++++++++++++++++++++++++++ qapi/meson.build | 1 + qapi/qapi-schema.json | 1 + qapi/uefi.json | 45 +++++++ 4 files changed, 290 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..761082c11fc1 --- /dev/null +++ b/hw/uefi/var-service-json.c @@ -0,0 +1,243 @@ +/* + * 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[] =3D { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', + }; + uint8_t *src =3D data; + char *dest; + size_t i; + + dest =3D g_malloc(len * 2 + 1); + for (i =3D 0; i < len * 2;) { + dest[i++] =3D hex[*src >> 4]; + dest[i++] =3D hex[*src & 15]; + src++; + } + dest[i++] =3D 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 =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 generate_hexstr(var->data, var->data_size); + + if (var->attributes & + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) { + v->time =3D generate_hexstr(&var->time, sizeof(var->time)); + if (var->digest && var->digest_size) { + v->digest =3D generate_hexstr(var->digest, var->digest_siz= e); + } + } + + 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 =3D dest; + size_t i; + + for (i =3D 0; i < len; i +=3D 2) { + *(data++) =3D + 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 =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); + parse_hexstr(var->data, v->data, len); + + if (v->time && strlen(v->time) =3D=3D 32) { + parse_hexstr(&var->time, v->time, 32); + } + + if (v->digest) { + len =3D strlen(v->digest); + var->digest_size =3D len / 2; + var->digest =3D 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 =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; + int rc; + + if (uv->jsonfd =3D=3D -1) { + return; + } + + gstr =3D uefi_vars_to_json(uv); + + lseek(uv->jsonfd, 0, SEEK_SET); + rc =3D ftruncate(uv->jsonfd, 0); + if (rc !=3D 0) { + warn_report("%s: ftruncate error", __func__); + } + rc =3D write(uv->jsonfd, gstr->str, gstr->len); + if (rc !=3D gstr->len) { + warn_report("%s: write error", __func__); + } + 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; + int rc; + + 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); + rc =3D read(uv->jsonfd, str, len); + if (rc !=3D len) { + warn_report("%s: read error", __func__); + } + 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); + uefi_vars_update_storage(uv); + } + + qapi_free_UefiVarStore(vs); + qobject_unref(qobj); + g_free(str); +} diff --git a/qapi/meson.build b/qapi/meson.build index e7bc54e5d047..eadde4db307f 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -65,6 +65,7 @@ if have_system 'pci', 'rocker', 'tpm', + 'uefi', ] endif if have_system or have_tools diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index b1581988e4eb..2877aff73d0c 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -81,3 +81,4 @@ { 'include': 'vfio.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..c268ed11b70c --- /dev/null +++ b/qapi/uefi.json @@ -0,0 +1,45 @@ +# -*- Mode: Python -*- +# vim: filetype=3Dpython +# + +## +# @UefiVariable: +# +# UEFI Variable +# +# @guid: variable namespace guid +# +# @name: variable name (utf-8) +# +# @attr: variable attributes +# +# @data: variable content (base64) +# +# @time: variable modification time (EFI_VARIABLE_TIME_BASED_AUTHENTICATED= _WRITE_ACCESS). +# +# @digest: variable certificate digest (EFI_VARIABLE_TIME_BASED_AUTHENTICA= TED_WRITE_ACCESS). +# +# Since: 10.0 +## +{ 'struct' : 'UefiVariable', + 'data' : { 'guid' : 'str', + 'name' : 'str', + 'attr' : 'int', + 'data' : 'str', + '*time' : 'str', + '*digest' : 'str'}} + +## +# @UefiVarStore: +# +# UEFI Variable Store +# +# @version: 2 +# +# @variables: list of uefi variables +# +# Since: 10.0 +## +{ 'struct' : 'UefiVarStore', + 'data' : { 'version' : 'int', + 'variables' : [ 'UefiVariable' ] }} --=20 2.48.1