[PATCH 2/2] hw/uefi: add pcap support

Gerd Hoffmann posted 2 patches 2 days, 19 hours ago
Maintainers: Gerd Hoffmann <kraxel@redhat.com>
There is a newer version of this series
[PATCH 2/2] hw/uefi: add pcap support
Posted by Gerd Hoffmann 2 days, 19 hours ago
Add pcapfile property to uevi-vars-* devices, allowing to write out a
capture of the communication traffic between uefi firmware and qemu.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 include/hw/uefi/var-service.h | 10 ++++
 hw/uefi/var-service-core.c    |  7 +++
 hw/uefi/var-service-pcap.c    | 94 +++++++++++++++++++++++++++++++++++
 hw/uefi/var-service-sysbus.c  |  1 +
 hw/uefi/meson.build           |  1 +
 roms/edk2                     |  2 +-
 6 files changed, 114 insertions(+), 1 deletion(-)
 create mode 100644 hw/uefi/var-service-pcap.c

diff --git a/include/hw/uefi/var-service.h b/include/hw/uefi/var-service.h
index 91fb4a20918a..116ee90a1146 100644
--- a/include/hw/uefi/var-service.h
+++ b/include/hw/uefi/var-service.h
@@ -77,6 +77,10 @@ struct uefi_vars_state {
     bool                              force_secure_boot;
     bool                              disable_custom_mode;
     bool                              use_pio;
+
+    /* request + reply capture */
+    char                              *pcapfile;
+    FILE                              *pcapfp;
 };
 
 struct uefi_vars_cert {
@@ -189,4 +193,10 @@ uefi_var_policy *uefi_vars_add_policy(uefi_vars_state *uv,
                                       variable_policy_entry *pe);
 uint32_t uefi_vars_mm_check_policy_proto(uefi_vars_state *uv);
 
+/* vars-service-pcap.c */
+void uefi_vars_pcap_init(uefi_vars_state *uv);
+void uefi_vars_pcap_reset(uefi_vars_state *uv);
+void uefi_vars_pcap_request(uefi_vars_state *uv, void *buffer, size_t size);
+void uefi_vars_pcap_reply(uefi_vars_state *uv, void *buffer, size_t size);
+
 #endif /* QEMU_UEFI_VAR_SERVICE_H */
diff --git a/hw/uefi/var-service-core.c b/hw/uefi/var-service-core.c
index 6ab8df091aaf..6d7913e03f45 100644
--- a/hw/uefi/var-service-core.c
+++ b/hw/uefi/var-service-core.c
@@ -101,6 +101,8 @@ static uint32_t uefi_vars_cmd_mm(uefi_vars_state *uv, bool dma_mode)
     }
     memset(uv->buffer + size, 0, uv->buf_size - size);
 
+    uefi_vars_pcap_request(uv, uv->buffer, size);
+
     /* dispatch */
     if (qemu_uuid_is_equal(&mhdr->guid, &EfiSmmVariableProtocolGuid)) {
         retval = uefi_vars_mm_vars_proto(uv);
@@ -127,6 +129,8 @@ static uint32_t uefi_vars_cmd_mm(uefi_vars_state *uv, bool dma_mode)
         retval = UEFI_VARS_STS_ERR_NOT_SUPPORTED;
     }
 
+    uefi_vars_pcap_reply(uv, uv->buffer, sizeof(*mhdr) + mhdr->length);
+
     /* write buffer */
     if (dma_mode) {
         dma_memory_write(&address_space_memory, dma,
@@ -163,6 +167,8 @@ void uefi_vars_hard_reset(uefi_vars_state *uv)
     uefi_vars_clear_volatile(uv);
     uefi_vars_policies_clear(uv);
     uefi_vars_auth_init(uv);
+
+    uefi_vars_pcap_reset(uv);
 }
 
 static uint32_t uefi_vars_cmd(uefi_vars_state *uv, uint32_t cmd)
@@ -319,4 +325,5 @@ void uefi_vars_realize(uefi_vars_state *uv, Error **errp)
 {
     uefi_vars_json_init(uv, errp);
     uefi_vars_json_load(uv, errp);
+    uefi_vars_pcap_init(uv);
 }
diff --git a/hw/uefi/var-service-pcap.c b/hw/uefi/var-service-pcap.c
new file mode 100644
index 000000000000..879eee4699a3
--- /dev/null
+++ b/hw/uefi/var-service-pcap.c
@@ -0,0 +1,94 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qemu/pcap.h"
+#include "system/dma.h"
+
+#include "hw/uefi/var-service.h"
+
+#define LINKTYPE_EDK2_MM  302
+
+#define SNAPLEN   (64 * 1024)
+#define TYPE_RESET       0x01
+#define TYPE_REQUEST     0x02
+#define TYPE_REPLY       0x03
+
+static void uefi_vars_pcap_header(FILE *fp)
+{
+    struct pcap_hdr header = {
+        .magic_number  = PCAP_MAGIC,
+        .version_major = PCAP_MAJOR,
+        .version_minor = PCAP_MINOR,
+        .snaplen       = SNAPLEN,
+        .network       = LINKTYPE_EDK2_MM,
+    };
+
+    fwrite(&header, sizeof(header), 1, fp);
+    fflush(fp);
+}
+
+static void uefi_vars_pcap_packet(FILE *fp, uint32_t type, void *buffer, size_t size)
+{
+    struct pcaprec_hdr header;
+    struct timeval tv;
+    uint32_t orig_len = size + sizeof(type);
+    uint32_t incl_len = MIN(orig_len, SNAPLEN);
+
+    gettimeofday(&tv, NULL);
+    header.ts_sec   = tv.tv_sec;
+    header.ts_usec  = tv.tv_usec;
+    header.incl_len = incl_len;
+    header.orig_len = orig_len;
+
+    fwrite(&header, sizeof(header), 1, fp);
+    fwrite(&type, sizeof(type), 1, fp);
+    if (buffer) {
+        fwrite(buffer, incl_len - sizeof(type), 1, fp);
+    }
+    fflush(fp);
+}
+
+void uefi_vars_pcap_init(uefi_vars_state *uv)
+{
+    int fd;
+
+    if (!uv->pcapfile) {
+        return;
+    }
+
+    fd = qemu_open_old(uv->pcapfile,
+                       O_CREAT | O_WRONLY | O_TRUNC | O_BINARY, 0666);
+    if (fd < 0) {
+        warn_report("open %s: %s", uv->pcapfile, strerror(errno));
+        return;
+    }
+
+    uv->pcapfp = fdopen(fd, "wb");
+    uefi_vars_pcap_header(uv->pcapfp);
+}
+
+void uefi_vars_pcap_reset(uefi_vars_state *uv)
+{
+    if (!uv->pcapfp) {
+        return;
+    }
+    uefi_vars_pcap_packet(uv->pcapfp, TYPE_RESET, NULL, 0);
+}
+
+void uefi_vars_pcap_request(uefi_vars_state *uv, void *buffer, size_t size)
+{
+    if (!uv->pcapfp) {
+        return;
+    }
+    uefi_vars_pcap_packet(uv->pcapfp, TYPE_REQUEST, buffer, size);
+}
+
+void uefi_vars_pcap_reply(uefi_vars_state *uv, void *buffer, size_t size)
+{
+    if (!uv->pcapfp) {
+        return;
+    }
+    uefi_vars_pcap_packet(uv->pcapfp, TYPE_REPLY, buffer, size);
+}
diff --git a/hw/uefi/var-service-sysbus.c b/hw/uefi/var-service-sysbus.c
index a5aa218e2600..bd37d5bd3526 100644
--- a/hw/uefi/var-service-sysbus.c
+++ b/hw/uefi/var-service-sysbus.c
@@ -33,6 +33,7 @@ static const Property uefi_vars_sysbus_properties[] = {
     DEFINE_PROP_SIZE("size", uefi_vars_sysbus_state, state.max_storage,
                      256 * 1024),
     DEFINE_PROP_STRING("jsonfile", uefi_vars_sysbus_state, state.jsonfile),
+    DEFINE_PROP_STRING("pcapfile", uefi_vars_sysbus_state, state.pcapfile),
     DEFINE_PROP_BOOL("force-secure-boot", uefi_vars_sysbus_state,
                      state.force_secure_boot, false),
     DEFINE_PROP_BOOL("disable-custom-mode", uefi_vars_sysbus_state,
diff --git a/hw/uefi/meson.build b/hw/uefi/meson.build
index c8f38dfae247..3eae47553315 100644
--- a/hw/uefi/meson.build
+++ b/hw/uefi/meson.build
@@ -3,6 +3,7 @@ system_ss.add(files('hardware-info.c', 'ovmf-log.c'))
 uefi_vars_ss = ss.source_set()
 if (config_all_devices.has_key('CONFIG_UEFI_VARS'))
   uefi_vars_ss.add(files('var-service-core.c',
+                         'var-service-pcap.c',
                          'var-service-json.c',
                          'var-service-vars.c',
                          'var-service-auth.c',
diff --git a/roms/edk2 b/roms/edk2
index 4dfdca63a934..46548b1adac8 160000
--- a/roms/edk2
+++ b/roms/edk2
@@ -1 +1 @@
-Subproject commit 4dfdca63a93497203f197ec98ba20e2327e4afe4
+Subproject commit 46548b1adac82211d8d11da12dd914f41e7aa775
-- 
2.52.0
Re: [PATCH 2/2] hw/uefi: add pcap support
Posted by Philippe Mathieu-Daudé 2 days, 4 hours ago
On 26/11/25 15:25, Gerd Hoffmann wrote:
> Add pcapfile property to uevi-vars-* devices, allowing to write out a
> capture of the communication traffic between uefi firmware and qemu.
> 
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
>   include/hw/uefi/var-service.h | 10 ++++
>   hw/uefi/var-service-core.c    |  7 +++
>   hw/uefi/var-service-pcap.c    | 94 +++++++++++++++++++++++++++++++++++
>   hw/uefi/var-service-sysbus.c  |  1 +
>   hw/uefi/meson.build           |  1 +
>   roms/edk2                     |  2 +-
>   6 files changed, 114 insertions(+), 1 deletion(-)
>   create mode 100644 hw/uefi/var-service-pcap.c


> diff --git a/hw/uefi/var-service-pcap.c b/hw/uefi/var-service-pcap.c
> new file mode 100644
> index 000000000000..879eee4699a3
> --- /dev/null
> +++ b/hw/uefi/var-service-pcap.c
> @@ -0,0 +1,94 @@
> +/*
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +#include "qemu/osdep.h"
> +#include "qemu/error-report.h"
> +#include "qemu/pcap.h"
> +#include "system/dma.h"
> +
> +#include "hw/uefi/var-service.h"
> +
> +#define LINKTYPE_EDK2_MM  302
> +
> +#define SNAPLEN   (64 * 1024)
> +#define TYPE_RESET       0x01
> +#define TYPE_REQUEST     0x02
> +#define TYPE_REPLY       0x03
> +
> +static void uefi_vars_pcap_header(FILE *fp)
> +{

static const

> +    struct pcap_hdr header = {
> +        .magic_number  = PCAP_MAGIC,
> +        .version_major = PCAP_MAJOR,
> +        .version_minor = PCAP_MINOR,
> +        .snaplen       = SNAPLEN,
> +        .network       = LINKTYPE_EDK2_MM,
> +    };
> +
> +    fwrite(&header, sizeof(header), 1, fp);
> +    fflush(fp);
> +}


> +void uefi_vars_pcap_init(uefi_vars_state *uv)
> +{
> +    int fd;
> +
> +    if (!uv->pcapfile) {
> +        return;
> +    }
> +
> +    fd = qemu_open_old(uv->pcapfile,
> +                       O_CREAT | O_WRONLY | O_TRUNC | O_BINARY, 0666);

Consider qemu_create() to help Markus' tree-wide cleanup.

> +    if (fd < 0) {
> +        warn_report("open %s: %s", uv->pcapfile, strerror(errno));
> +        return;
> +    }
> +
> +    uv->pcapfp = fdopen(fd, "wb");
> +    uefi_vars_pcap_header(uv->pcapfp);
> +}


> diff --git a/roms/edk2 b/roms/edk2
> index 4dfdca63a934..46548b1adac8 160000
> --- a/roms/edk2
> +++ b/roms/edk2
> @@ -1 +1 @@
> -Subproject commit 4dfdca63a93497203f197ec98ba20e2327e4afe4
> +Subproject commit 46548b1adac82211d8d11da12dd914f41e7aa775

Unrelated change I presume.

Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>