* [PATCH v2] hw/uefi: add "info ovmf-log" + "query-ovmf-log" monitor commands
@ 2025-10-07 13:52 Gerd Hoffmann
2025-10-08 6:32 ` Markus Armbruster
2025-10-09 1:56 ` Dr. David Alan Gilbert
0 siblings, 2 replies; 10+ messages in thread
From: Gerd Hoffmann @ 2025-10-07 13:52 UTC (permalink / raw)
To: qemu-devel
Cc: Laurent Vivier, Marcel Apfelbaum, Markus Armbruster,
Philippe Mathieu-Daudé, Yanan Wang, Gerd Hoffmann,
Paolo Bonzini, Dr. David Alan Gilbert, Fabiano Rosas, Eric Blake,
Eduardo Habkost, Zhao Liu
Starting with the edk2-stable202508 tag OVMF (and ArmVirt too) have
optional support for logging to a memory buffer. There is guest side
support -- for example in linux kernels v6.17+ -- to read that buffer.
But that might not helpful if your guest stops booting early enough that
guest tooling can not be used yet. So host side support to read that
log buffer is a useful thing to have.
This patch implements both qmp and hmp monitor commands to read the
firmware log.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
hw/uefi/ovmf-log.c | 237 +++++++++++++++++++++++++++++++++++++
tests/qtest/qmp-cmd-test.c | 2 +
hmp-commands-info.hx | 13 ++
hw/uefi/meson.build | 2 +-
qapi/machine.json | 10 ++
5 files changed, 263 insertions(+), 1 deletion(-)
create mode 100644 hw/uefi/ovmf-log.c
diff --git a/hw/uefi/ovmf-log.c b/hw/uefi/ovmf-log.c
new file mode 100644
index 000000000000..f7fdb1f6bcad
--- /dev/null
+++ b/hw/uefi/ovmf-log.c
@@ -0,0 +1,237 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * print ovmf debug log
+ *
+ * see OvmfPkg/Library/MemDebugLogLib/ in edk2
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qemu/target-info-qapi.h"
+#include "hw/boards.h"
+#include "hw/i386/x86.h"
+#include "hw/arm/virt.h"
+#include "system/dma.h"
+#include "monitor/hmp.h"
+#include "monitor/monitor.h"
+#include "qapi/error.h"
+#include "qapi/type-helpers.h"
+#include "qapi/qapi-commands-machine.h"
+
+
+/* ----------------------------------------------------------------------- */
+/* copy from edk2 */
+
+#define MEM_DEBUG_LOG_MAGIC1 0x3167646d666d766f /* "ovmfmdg1" */
+#define MEM_DEBUG_LOG_MAGIC2 0x3267646d666d766f /* "ovmfmdg2" */
+
+/*
+ * Mem Debug Log buffer header.
+ * The Log buffer is circular. Only the most
+ * recent messages are retained. Older messages
+ * will be discarded if the buffer overflows.
+ * The Debug Log starts just after the header.
+ */
+typedef struct {
+ /*
+ * Magic values
+ * These fields are used by tools to locate the buffer in
+ * memory. These MUST be the first two fields of the structure.
+ * Use a 128 bit Magic to vastly reduce the possibility of
+ * a collision with random data in memory.
+ */
+ uint64_t Magic1;
+ uint64_t Magic2;
+ /*
+ * Header Size
+ * This MUST be the third field of the structure
+ */
+ uint64_t HeaderSize;
+ /*
+ * Debug log size (minus header)
+ */
+ uint64_t DebugLogSize;
+ /*
+ * edk2 uses this for locking access.
+ */
+ uint64_t MemDebugLogLock;
+ /*
+ * Debug log head offset
+ */
+ uint64_t DebugLogHeadOffset;
+ /*
+ * Debug log tail offset
+ */
+ uint64_t DebugLogTailOffset;
+ /*
+ * Flag to indicate if the buffer wrapped and was thus truncated.
+ */
+ uint64_t Truncated;
+ /*
+ * Firmware Build Version (PcdFirmwareVersionString)
+ */
+ char FirmwareVersion[128];
+} MEM_DEBUG_LOG_HDR;
+
+
+/* ----------------------------------------------------------------------- */
+/* qemu monitor command */
+
+typedef struct {
+ uint64_t Magic1;
+ uint64_t Magic2;
+} MEM_DEBUG_LOG_MAGIC;
+
+/* find log buffer in guest memory by searching for the magic cookie */
+static dma_addr_t find_ovmf_log_range(dma_addr_t start, dma_addr_t end)
+{
+ static const MEM_DEBUG_LOG_MAGIC magic = {
+ .Magic1 = MEM_DEBUG_LOG_MAGIC1,
+ .Magic2 = MEM_DEBUG_LOG_MAGIC2,
+ };
+ MEM_DEBUG_LOG_MAGIC check;
+ dma_addr_t step = 4 * KiB;
+ dma_addr_t offset;
+
+ for (offset = start; offset < end; offset += step) {
+ if (dma_memory_read(&address_space_memory, offset,
+ &check, sizeof(check),
+ MEMTXATTRS_UNSPECIFIED)) {
+ /* dma error -> stop searching */
+ break;
+ }
+ if (memcmp(&magic, &check, sizeof(check)) == 0) {
+ return offset;
+ }
+ }
+ return -1;
+}
+
+static dma_addr_t find_ovmf_log(void)
+{
+ MachineState *ms = MACHINE(qdev_get_machine());
+ dma_addr_t start, end, offset;
+
+ if (target_arch() == SYS_EMU_TARGET_X86_64 &&
+ object_dynamic_cast(OBJECT(ms), TYPE_X86_MACHINE)) {
+ X86MachineState *x86ms = X86_MACHINE(ms);
+
+ /* early log buffer, static allocation in memfd, sec + early pei */
+ offset = find_ovmf_log_range(0x800000, 0x900000);
+ if (offset != -1) {
+ return offset;
+ }
+
+ /*
+ * normal log buffer, dynamically allocated close to end of low memory,
+ * late pei + dxe phase
+ */
+ end = x86ms->below_4g_mem_size;
+ start = end - MIN(end, 128 * MiB);
+ offset = find_ovmf_log_range(start, end);
+ return offset;
+ }
+
+ if (target_arch() == SYS_EMU_TARGET_AARCH64 &&
+ object_dynamic_cast(OBJECT(ms), TYPE_VIRT_MACHINE)) {
+ /* edk2 ArmVirt firmware allocations are in the first 128 MB */
+ VirtMachineState *vms = VIRT_MACHINE(ms);
+ start = vms->memmap[VIRT_MEM].base;
+ end = start + 128 * MiB;
+ offset = find_ovmf_log_range(start, end);
+ return offset;
+ }
+
+ return -1;
+}
+
+static void handle_ovmf_log_range(GString *out,
+ dma_addr_t start,
+ dma_addr_t end,
+ Error **errp)
+{
+ g_autofree char *buf = NULL;
+
+ if (start > end) {
+ return;
+ }
+
+ buf = g_malloc(end - start + 1);
+ if (dma_memory_read(&address_space_memory, start,
+ buf, end - start,
+ MEMTXATTRS_UNSPECIFIED)) {
+ error_setg(errp, "firmware log: buffer read error");
+ return;
+ }
+
+ buf[end - start] = 0;
+ g_string_append_printf(out, "%s", buf);
+}
+
+HumanReadableText *qmp_query_ovmf_log(Error **errp)
+{
+ MEM_DEBUG_LOG_HDR header;
+ dma_addr_t offset, base;
+ g_autoptr(GString) out = g_string_new("");
+
+ offset = find_ovmf_log();
+ if (offset == -1) {
+ error_setg(errp, "firmware log: not found");
+ goto err;
+ }
+
+ if (dma_memory_read(&address_space_memory, offset,
+ &header, sizeof(header),
+ MEMTXATTRS_UNSPECIFIED)) {
+ error_setg(errp, "firmware log: header read error");
+ goto err;
+ }
+
+ if (header.DebugLogSize > MiB) {
+ /* default size is 128k (32 pages), allow up to 1M */
+ error_setg(errp, "firmware log: log buffer is too big");
+ goto err;
+ }
+
+ if (header.DebugLogHeadOffset > header.DebugLogSize ||
+ header.DebugLogTailOffset > header.DebugLogSize) {
+ error_setg(errp, "firmware log: invalid header");
+ goto err;
+ }
+
+ g_string_append_printf(out, "firmware log: version \"%s\"\n",
+ header.FirmwareVersion);
+
+ base = offset + header.HeaderSize;
+ if (header.DebugLogHeadOffset > header.DebugLogTailOffset) {
+ /* wrap around */
+ handle_ovmf_log_range(out,
+ base + header.DebugLogHeadOffset,
+ base + header.DebugLogSize,
+ errp);
+ if (*errp) {
+ goto err;
+ }
+ handle_ovmf_log_range(out,
+ base + 0,
+ base + header.DebugLogTailOffset,
+ errp);
+ if (*errp) {
+ goto err;
+ }
+ } else {
+ handle_ovmf_log_range(out,
+ base + header.DebugLogHeadOffset,
+ base + header.DebugLogTailOffset,
+ errp);
+ if (*errp) {
+ goto err;
+ }
+ }
+
+ return human_readable_text_from_str(out);
+
+err:
+ return NULL;
+}
diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c
index cf718761861d..ffdb7e979e0f 100644
--- a/tests/qtest/qmp-cmd-test.c
+++ b/tests/qtest/qmp-cmd-test.c
@@ -52,6 +52,8 @@ static int query_error_class(const char *cmd)
/* Only valid with accel=tcg */
{ "x-query-jit", ERROR_CLASS_GENERIC_ERROR },
{ "xen-event-list", ERROR_CLASS_GENERIC_ERROR },
+ /* requires firmware with memory buffer logging support */
+ { "query-ovmf-log", ERROR_CLASS_GENERIC_ERROR },
{ NULL, -1 }
};
int i;
diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
index 6142f60e7b16..eca0614903d1 100644
--- a/hmp-commands-info.hx
+++ b/hmp-commands-info.hx
@@ -977,3 +977,16 @@ SRST
``info cryptodev``
Show the crypto devices.
ERST
+
+ {
+ .name = "ovmf-log",
+ .args_type = "",
+ .params = "",
+ .help = "show the ovmf debug log",
+ .cmd_info_hrt = qmp_query_ovmf_log,
+ },
+
+SRST
+ ``info ovmf-log``
+ Show the ovmf debug log.
+ERST
diff --git a/hw/uefi/meson.build b/hw/uefi/meson.build
index 91eb95f89e6d..c8f38dfae247 100644
--- a/hw/uefi/meson.build
+++ b/hw/uefi/meson.build
@@ -1,4 +1,4 @@
-system_ss.add(files('hardware-info.c'))
+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'))
diff --git a/qapi/machine.json b/qapi/machine.json
index 038eab281c78..329034035029 100644
--- a/qapi/machine.json
+++ b/qapi/machine.json
@@ -1839,6 +1839,16 @@
'returns': 'HumanReadableText',
'features': [ 'unstable' ]}
+##
+# @query-ovmf-log:
+#
+# Find firmware memory log buffer in guest memory, return content.
+#
+# Since: 10.2
+##
+{ 'command': 'query-ovmf-log',
+ 'returns': 'HumanReadableText' }
+
##
# @dump-skeys:
#
--
2.51.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v2] hw/uefi: add "info ovmf-log" + "query-ovmf-log" monitor commands
2025-10-07 13:52 [PATCH v2] hw/uefi: add "info ovmf-log" + "query-ovmf-log" monitor commands Gerd Hoffmann
@ 2025-10-08 6:32 ` Markus Armbruster
2025-10-08 7:09 ` Daniel P. Berrangé
2025-10-09 1:56 ` Dr. David Alan Gilbert
1 sibling, 1 reply; 10+ messages in thread
From: Markus Armbruster @ 2025-10-08 6:32 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: qemu-devel, Laurent Vivier, Marcel Apfelbaum,
Philippe Mathieu-Daudé, Yanan Wang, Paolo Bonzini,
Dr. David Alan Gilbert, Fabiano Rosas, Eric Blake,
Eduardo Habkost, Zhao Liu
Gerd Hoffmann <kraxel@redhat.com> writes:
> Starting with the edk2-stable202508 tag OVMF (and ArmVirt too) have
> optional support for logging to a memory buffer. There is guest side
> support -- for example in linux kernels v6.17+ -- to read that buffer.
> But that might not helpful if your guest stops booting early enough that
> guest tooling can not be used yet. So host side support to read that
> log buffer is a useful thing to have.
>
> This patch implements both qmp and hmp monitor commands to read the
> firmware log.
>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
[...]
> diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c
> index cf718761861d..ffdb7e979e0f 100644
> --- a/tests/qtest/qmp-cmd-test.c
> +++ b/tests/qtest/qmp-cmd-test.c
> @@ -52,6 +52,8 @@ static int query_error_class(const char *cmd)
> /* Only valid with accel=tcg */
> { "x-query-jit", ERROR_CLASS_GENERIC_ERROR },
> { "xen-event-list", ERROR_CLASS_GENERIC_ERROR },
> + /* requires firmware with memory buffer logging support */
> + { "query-ovmf-log", ERROR_CLASS_GENERIC_ERROR },
> { NULL, -1 }
> };
> int i;
Makes sense.
> diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
> index 6142f60e7b16..eca0614903d1 100644
> --- a/hmp-commands-info.hx
> +++ b/hmp-commands-info.hx
> @@ -977,3 +977,16 @@ SRST
> ``info cryptodev``
> Show the crypto devices.
> ERST
> +
> + {
> + .name = "ovmf-log",
> + .args_type = "",
> + .params = "",
> + .help = "show the ovmf debug log",
> + .cmd_info_hrt = qmp_query_ovmf_log,
> + },
> +
> +SRST
> + ``info ovmf-log``
> + Show the ovmf debug log.
> +ERST
> diff --git a/hw/uefi/meson.build b/hw/uefi/meson.build
> index 91eb95f89e6d..c8f38dfae247 100644
> --- a/hw/uefi/meson.build
> +++ b/hw/uefi/meson.build
> @@ -1,4 +1,4 @@
> -system_ss.add(files('hardware-info.c'))
> +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'))
> diff --git a/qapi/machine.json b/qapi/machine.json
> index 038eab281c78..329034035029 100644
> --- a/qapi/machine.json
> +++ b/qapi/machine.json
> @@ -1839,6 +1839,16 @@
> 'returns': 'HumanReadableText',
> 'features': [ 'unstable' ]}
>
> +##
> +# @query-ovmf-log:
> +#
> +# Find firmware memory log buffer in guest memory, return content.
> +#
> +# Since: 10.2
> +##
> +{ 'command': 'query-ovmf-log',
> + 'returns': 'HumanReadableText' }
All other commands returning HumanReadableText are unstable. Does this
one need to be stable? If yes, why?
> +
> ##
> # @dump-skeys:
> #
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2] hw/uefi: add "info ovmf-log" + "query-ovmf-log" monitor commands
2025-10-08 6:32 ` Markus Armbruster
@ 2025-10-08 7:09 ` Daniel P. Berrangé
0 siblings, 0 replies; 10+ messages in thread
From: Daniel P. Berrangé @ 2025-10-08 7:09 UTC (permalink / raw)
To: Markus Armbruster
Cc: Gerd Hoffmann, qemu-devel, Laurent Vivier, Marcel Apfelbaum,
Philippe Mathieu-Daudé, Yanan Wang, Paolo Bonzini,
Dr. David Alan Gilbert, Fabiano Rosas, Eric Blake,
Eduardo Habkost, Zhao Liu
On Wed, Oct 08, 2025 at 08:32:39AM +0200, Markus Armbruster wrote:
> Gerd Hoffmann <kraxel@redhat.com> writes:
>
> > Starting with the edk2-stable202508 tag OVMF (and ArmVirt too) have
> > optional support for logging to a memory buffer. There is guest side
> > support -- for example in linux kernels v6.17+ -- to read that buffer.
> > But that might not helpful if your guest stops booting early enough that
> > guest tooling can not be used yet. So host side support to read that
> > log buffer is a useful thing to have.
> >
> > This patch implements both qmp and hmp monitor commands to read the
> > firmware log.
> >
> > Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
>
> [...]
>
> > diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c
> > index cf718761861d..ffdb7e979e0f 100644
> > --- a/tests/qtest/qmp-cmd-test.c
> > +++ b/tests/qtest/qmp-cmd-test.c
> > @@ -52,6 +52,8 @@ static int query_error_class(const char *cmd)
> > /* Only valid with accel=tcg */
> > { "x-query-jit", ERROR_CLASS_GENERIC_ERROR },
> > { "xen-event-list", ERROR_CLASS_GENERIC_ERROR },
> > + /* requires firmware with memory buffer logging support */
> > + { "query-ovmf-log", ERROR_CLASS_GENERIC_ERROR },
> > { NULL, -1 }
> > };
> > int i;
>
> Makes sense.
>
> > diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
> > index 6142f60e7b16..eca0614903d1 100644
> > --- a/hmp-commands-info.hx
> > +++ b/hmp-commands-info.hx
> > @@ -977,3 +977,16 @@ SRST
> > ``info cryptodev``
> > Show the crypto devices.
> > ERST
> > +
> > + {
> > + .name = "ovmf-log",
> > + .args_type = "",
> > + .params = "",
> > + .help = "show the ovmf debug log",
> > + .cmd_info_hrt = qmp_query_ovmf_log,
> > + },
> > +
> > +SRST
> > + ``info ovmf-log``
> > + Show the ovmf debug log.
> > +ERST
> > diff --git a/hw/uefi/meson.build b/hw/uefi/meson.build
> > index 91eb95f89e6d..c8f38dfae247 100644
> > --- a/hw/uefi/meson.build
> > +++ b/hw/uefi/meson.build
> > @@ -1,4 +1,4 @@
> > -system_ss.add(files('hardware-info.c'))
> > +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'))
> > diff --git a/qapi/machine.json b/qapi/machine.json
> > index 038eab281c78..329034035029 100644
> > --- a/qapi/machine.json
> > +++ b/qapi/machine.json
> > @@ -1839,6 +1839,16 @@
> > 'returns': 'HumanReadableText',
> > 'features': [ 'unstable' ]}
> >
> > +##
> > +# @query-ovmf-log:
> > +#
> > +# Find firmware memory log buffer in guest memory, return content.
> > +#
> > +# Since: 10.2
> > +##
> > +{ 'command': 'query-ovmf-log',
> > + 'returns': 'HumanReadableText' }
>
> All other commands returning HumanReadableText are unstable. Does this
> one need to be stable? If yes, why?
The main reason why all the others are 'unstable' is that I did a "blind"
conversion of "info XXXX" to "x-query-XXXX", with no consideration for
the data design. They are all returning structured data munged into an
opaque string targetted at humans, not machines.
For this log command the data is inherantly a string to begin with, so
the general problem data design doesn't apply here. This command is
suitable for consumption both by machines and humans. Overall I think
it is reasonable to consider this new command stable.
Maybe this means we should return 'str' instead of 'HumanReadableText'
to distinguish it ?
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2] hw/uefi: add "info ovmf-log" + "query-ovmf-log" monitor commands
2025-10-07 13:52 [PATCH v2] hw/uefi: add "info ovmf-log" + "query-ovmf-log" monitor commands Gerd Hoffmann
2025-10-08 6:32 ` Markus Armbruster
@ 2025-10-09 1:56 ` Dr. David Alan Gilbert
2025-10-09 7:13 ` Daniel P. Berrangé
1 sibling, 1 reply; 10+ messages in thread
From: Dr. David Alan Gilbert @ 2025-10-09 1:56 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: qemu-devel, Laurent Vivier, Marcel Apfelbaum, Markus Armbruster,
Philippe Mathieu-Daudé, Yanan Wang, Paolo Bonzini,
Fabiano Rosas, Eric Blake, Eduardo Habkost, Zhao Liu
* Gerd Hoffmann (kraxel@redhat.com) wrote:
> Starting with the edk2-stable202508 tag OVMF (and ArmVirt too) have
> optional support for logging to a memory buffer. There is guest side
> support -- for example in linux kernels v6.17+ -- to read that buffer.
> But that might not helpful if your guest stops booting early enough that
> guest tooling can not be used yet. So host side support to read that
> log buffer is a useful thing to have.
>
> This patch implements both qmp and hmp monitor commands to read the
> firmware log.
>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
I'm OK with that, but I wonder if it would be better to have a command
that wrote the buffer to a file rather than displaying it directly; I don't
think we normally have anything else which outputs that much raw guest
provided data directly.
I assume when it goes wrong you end up with random unprintable junk in
the buffer.
Dave
> ---
> hw/uefi/ovmf-log.c | 237 +++++++++++++++++++++++++++++++++++++
> tests/qtest/qmp-cmd-test.c | 2 +
> hmp-commands-info.hx | 13 ++
> hw/uefi/meson.build | 2 +-
> qapi/machine.json | 10 ++
> 5 files changed, 263 insertions(+), 1 deletion(-)
> create mode 100644 hw/uefi/ovmf-log.c
>
> diff --git a/hw/uefi/ovmf-log.c b/hw/uefi/ovmf-log.c
> new file mode 100644
> index 000000000000..f7fdb1f6bcad
> --- /dev/null
> +++ b/hw/uefi/ovmf-log.c
> @@ -0,0 +1,237 @@
> +/*
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * print ovmf debug log
> + *
> + * see OvmfPkg/Library/MemDebugLogLib/ in edk2
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/units.h"
> +#include "qemu/target-info-qapi.h"
> +#include "hw/boards.h"
> +#include "hw/i386/x86.h"
> +#include "hw/arm/virt.h"
> +#include "system/dma.h"
> +#include "monitor/hmp.h"
> +#include "monitor/monitor.h"
> +#include "qapi/error.h"
> +#include "qapi/type-helpers.h"
> +#include "qapi/qapi-commands-machine.h"
> +
> +
> +/* ----------------------------------------------------------------------- */
> +/* copy from edk2 */
> +
> +#define MEM_DEBUG_LOG_MAGIC1 0x3167646d666d766f /* "ovmfmdg1" */
> +#define MEM_DEBUG_LOG_MAGIC2 0x3267646d666d766f /* "ovmfmdg2" */
> +
> +/*
> + * Mem Debug Log buffer header.
> + * The Log buffer is circular. Only the most
> + * recent messages are retained. Older messages
> + * will be discarded if the buffer overflows.
> + * The Debug Log starts just after the header.
> + */
> +typedef struct {
> + /*
> + * Magic values
> + * These fields are used by tools to locate the buffer in
> + * memory. These MUST be the first two fields of the structure.
> + * Use a 128 bit Magic to vastly reduce the possibility of
> + * a collision with random data in memory.
> + */
> + uint64_t Magic1;
> + uint64_t Magic2;
> + /*
> + * Header Size
> + * This MUST be the third field of the structure
> + */
> + uint64_t HeaderSize;
> + /*
> + * Debug log size (minus header)
> + */
> + uint64_t DebugLogSize;
> + /*
> + * edk2 uses this for locking access.
> + */
> + uint64_t MemDebugLogLock;
> + /*
> + * Debug log head offset
> + */
> + uint64_t DebugLogHeadOffset;
> + /*
> + * Debug log tail offset
> + */
> + uint64_t DebugLogTailOffset;
> + /*
> + * Flag to indicate if the buffer wrapped and was thus truncated.
> + */
> + uint64_t Truncated;
> + /*
> + * Firmware Build Version (PcdFirmwareVersionString)
> + */
> + char FirmwareVersion[128];
> +} MEM_DEBUG_LOG_HDR;
> +
> +
> +/* ----------------------------------------------------------------------- */
> +/* qemu monitor command */
> +
> +typedef struct {
> + uint64_t Magic1;
> + uint64_t Magic2;
> +} MEM_DEBUG_LOG_MAGIC;
> +
> +/* find log buffer in guest memory by searching for the magic cookie */
> +static dma_addr_t find_ovmf_log_range(dma_addr_t start, dma_addr_t end)
> +{
> + static const MEM_DEBUG_LOG_MAGIC magic = {
> + .Magic1 = MEM_DEBUG_LOG_MAGIC1,
> + .Magic2 = MEM_DEBUG_LOG_MAGIC2,
> + };
> + MEM_DEBUG_LOG_MAGIC check;
> + dma_addr_t step = 4 * KiB;
> + dma_addr_t offset;
> +
> + for (offset = start; offset < end; offset += step) {
> + if (dma_memory_read(&address_space_memory, offset,
> + &check, sizeof(check),
> + MEMTXATTRS_UNSPECIFIED)) {
> + /* dma error -> stop searching */
> + break;
> + }
> + if (memcmp(&magic, &check, sizeof(check)) == 0) {
> + return offset;
> + }
> + }
> + return -1;
> +}
> +
> +static dma_addr_t find_ovmf_log(void)
> +{
> + MachineState *ms = MACHINE(qdev_get_machine());
> + dma_addr_t start, end, offset;
> +
> + if (target_arch() == SYS_EMU_TARGET_X86_64 &&
> + object_dynamic_cast(OBJECT(ms), TYPE_X86_MACHINE)) {
> + X86MachineState *x86ms = X86_MACHINE(ms);
> +
> + /* early log buffer, static allocation in memfd, sec + early pei */
> + offset = find_ovmf_log_range(0x800000, 0x900000);
> + if (offset != -1) {
> + return offset;
> + }
> +
> + /*
> + * normal log buffer, dynamically allocated close to end of low memory,
> + * late pei + dxe phase
> + */
> + end = x86ms->below_4g_mem_size;
> + start = end - MIN(end, 128 * MiB);
> + offset = find_ovmf_log_range(start, end);
> + return offset;
> + }
> +
> + if (target_arch() == SYS_EMU_TARGET_AARCH64 &&
> + object_dynamic_cast(OBJECT(ms), TYPE_VIRT_MACHINE)) {
> + /* edk2 ArmVirt firmware allocations are in the first 128 MB */
> + VirtMachineState *vms = VIRT_MACHINE(ms);
> + start = vms->memmap[VIRT_MEM].base;
> + end = start + 128 * MiB;
> + offset = find_ovmf_log_range(start, end);
> + return offset;
> + }
> +
> + return -1;
> +}
> +
> +static void handle_ovmf_log_range(GString *out,
> + dma_addr_t start,
> + dma_addr_t end,
> + Error **errp)
> +{
> + g_autofree char *buf = NULL;
> +
> + if (start > end) {
> + return;
> + }
> +
> + buf = g_malloc(end - start + 1);
> + if (dma_memory_read(&address_space_memory, start,
> + buf, end - start,
> + MEMTXATTRS_UNSPECIFIED)) {
> + error_setg(errp, "firmware log: buffer read error");
> + return;
> + }
> +
> + buf[end - start] = 0;
> + g_string_append_printf(out, "%s", buf);
> +}
> +
> +HumanReadableText *qmp_query_ovmf_log(Error **errp)
> +{
> + MEM_DEBUG_LOG_HDR header;
> + dma_addr_t offset, base;
> + g_autoptr(GString) out = g_string_new("");
> +
> + offset = find_ovmf_log();
> + if (offset == -1) {
> + error_setg(errp, "firmware log: not found");
> + goto err;
> + }
> +
> + if (dma_memory_read(&address_space_memory, offset,
> + &header, sizeof(header),
> + MEMTXATTRS_UNSPECIFIED)) {
> + error_setg(errp, "firmware log: header read error");
> + goto err;
> + }
> +
> + if (header.DebugLogSize > MiB) {
> + /* default size is 128k (32 pages), allow up to 1M */
> + error_setg(errp, "firmware log: log buffer is too big");
> + goto err;
> + }
> +
> + if (header.DebugLogHeadOffset > header.DebugLogSize ||
> + header.DebugLogTailOffset > header.DebugLogSize) {
> + error_setg(errp, "firmware log: invalid header");
> + goto err;
> + }
> +
> + g_string_append_printf(out, "firmware log: version \"%s\"\n",
> + header.FirmwareVersion);
> +
> + base = offset + header.HeaderSize;
> + if (header.DebugLogHeadOffset > header.DebugLogTailOffset) {
> + /* wrap around */
> + handle_ovmf_log_range(out,
> + base + header.DebugLogHeadOffset,
> + base + header.DebugLogSize,
> + errp);
> + if (*errp) {
> + goto err;
> + }
> + handle_ovmf_log_range(out,
> + base + 0,
> + base + header.DebugLogTailOffset,
> + errp);
> + if (*errp) {
> + goto err;
> + }
> + } else {
> + handle_ovmf_log_range(out,
> + base + header.DebugLogHeadOffset,
> + base + header.DebugLogTailOffset,
> + errp);
> + if (*errp) {
> + goto err;
> + }
> + }
> +
> + return human_readable_text_from_str(out);
> +
> +err:
> + return NULL;
> +}
> diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c
> index cf718761861d..ffdb7e979e0f 100644
> --- a/tests/qtest/qmp-cmd-test.c
> +++ b/tests/qtest/qmp-cmd-test.c
> @@ -52,6 +52,8 @@ static int query_error_class(const char *cmd)
> /* Only valid with accel=tcg */
> { "x-query-jit", ERROR_CLASS_GENERIC_ERROR },
> { "xen-event-list", ERROR_CLASS_GENERIC_ERROR },
> + /* requires firmware with memory buffer logging support */
> + { "query-ovmf-log", ERROR_CLASS_GENERIC_ERROR },
> { NULL, -1 }
> };
> int i;
> diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
> index 6142f60e7b16..eca0614903d1 100644
> --- a/hmp-commands-info.hx
> +++ b/hmp-commands-info.hx
> @@ -977,3 +977,16 @@ SRST
> ``info cryptodev``
> Show the crypto devices.
> ERST
> +
> + {
> + .name = "ovmf-log",
> + .args_type = "",
> + .params = "",
> + .help = "show the ovmf debug log",
> + .cmd_info_hrt = qmp_query_ovmf_log,
> + },
> +
> +SRST
> + ``info ovmf-log``
> + Show the ovmf debug log.
> +ERST
> diff --git a/hw/uefi/meson.build b/hw/uefi/meson.build
> index 91eb95f89e6d..c8f38dfae247 100644
> --- a/hw/uefi/meson.build
> +++ b/hw/uefi/meson.build
> @@ -1,4 +1,4 @@
> -system_ss.add(files('hardware-info.c'))
> +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'))
> diff --git a/qapi/machine.json b/qapi/machine.json
> index 038eab281c78..329034035029 100644
> --- a/qapi/machine.json
> +++ b/qapi/machine.json
> @@ -1839,6 +1839,16 @@
> 'returns': 'HumanReadableText',
> 'features': [ 'unstable' ]}
>
> +##
> +# @query-ovmf-log:
> +#
> +# Find firmware memory log buffer in guest memory, return content.
> +#
> +# Since: 10.2
> +##
> +{ 'command': 'query-ovmf-log',
> + 'returns': 'HumanReadableText' }
> +
> ##
> # @dump-skeys:
> #
> --
> 2.51.0
>
>
--
-----Open up your eyes, open up your mind, open up your code -------
/ Dr. David Alan Gilbert | Running GNU/Linux | Happy \
\ dave @ treblig.org | | In Hex /
\ _________________________|_____ http://www.treblig.org |_______/
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2] hw/uefi: add "info ovmf-log" + "query-ovmf-log" monitor commands
2025-10-09 1:56 ` Dr. David Alan Gilbert
@ 2025-10-09 7:13 ` Daniel P. Berrangé
2025-10-09 11:41 ` Dr. David Alan Gilbert
0 siblings, 1 reply; 10+ messages in thread
From: Daniel P. Berrangé @ 2025-10-09 7:13 UTC (permalink / raw)
To: Dr. David Alan Gilbert
Cc: Gerd Hoffmann, qemu-devel, Laurent Vivier, Marcel Apfelbaum,
Markus Armbruster, Philippe Mathieu-Daudé, Yanan Wang,
Paolo Bonzini, Fabiano Rosas, Eric Blake, Eduardo Habkost,
Zhao Liu
On Thu, Oct 09, 2025 at 01:56:09AM +0000, Dr. David Alan Gilbert wrote:
> * Gerd Hoffmann (kraxel@redhat.com) wrote:
> > Starting with the edk2-stable202508 tag OVMF (and ArmVirt too) have
> > optional support for logging to a memory buffer. There is guest side
> > support -- for example in linux kernels v6.17+ -- to read that buffer.
> > But that might not helpful if your guest stops booting early enough that
> > guest tooling can not be used yet. So host side support to read that
> > log buffer is a useful thing to have.
> >
> > This patch implements both qmp and hmp monitor commands to read the
> > firmware log.
> >
> > Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
>
> I'm OK with that, but I wonder if it would be better to have a command
> that wrote the buffer to a file rather than displaying it directly; I don't
> think we normally have anything else which outputs that much raw guest
> provided data directly.
> I assume when it goes wrong you end up with random unprintable junk in
> the buffer.
128 KB is on the high side, but is not terrible. Libvirt (arbitrarily)
caps a QMP reply at 10 MB. Libvirt is going to want to send this on to
the client app and will likely do that streaming in memory, so having
it iin a file is not required from our POV.
IIRC, some of the query-block command replies can get insanely huge
when the qcow2 chain is very long.
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2] hw/uefi: add "info ovmf-log" + "query-ovmf-log" monitor commands
2025-10-09 7:13 ` Daniel P. Berrangé
@ 2025-10-09 11:41 ` Dr. David Alan Gilbert
2025-10-09 12:00 ` Daniel P. Berrangé
0 siblings, 1 reply; 10+ messages in thread
From: Dr. David Alan Gilbert @ 2025-10-09 11:41 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: Gerd Hoffmann, qemu-devel, Laurent Vivier, Marcel Apfelbaum,
Markus Armbruster, Philippe Mathieu-Daudé, Yanan Wang,
Paolo Bonzini, Fabiano Rosas, Eric Blake, Eduardo Habkost,
Zhao Liu
* Daniel P. Berrangé (berrange@redhat.com) wrote:
> On Thu, Oct 09, 2025 at 01:56:09AM +0000, Dr. David Alan Gilbert wrote:
> > * Gerd Hoffmann (kraxel@redhat.com) wrote:
> > > Starting with the edk2-stable202508 tag OVMF (and ArmVirt too) have
> > > optional support for logging to a memory buffer. There is guest side
> > > support -- for example in linux kernels v6.17+ -- to read that buffer.
> > > But that might not helpful if your guest stops booting early enough that
> > > guest tooling can not be used yet. So host side support to read that
> > > log buffer is a useful thing to have.
> > >
> > > This patch implements both qmp and hmp monitor commands to read the
> > > firmware log.
> > >
> > > Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> >
> > I'm OK with that, but I wonder if it would be better to have a command
> > that wrote the buffer to a file rather than displaying it directly; I don't
> > think we normally have anything else which outputs that much raw guest
> > provided data directly.
> > I assume when it goes wrong you end up with random unprintable junk in
> > the buffer.
>
> 128 KB is on the high side, but is not terrible. Libvirt (arbitrarily)
> caps a QMP reply at 10 MB. Libvirt is going to want to send this on to
> the client app and will likely do that streaming in memory, so having
> it iin a file is not required from our POV.
>
> IIRC, some of the query-block command replies can get insanely huge
> when the qcow2 chain is very long.
OK, what about sanitisation - if that text contains random binary what happens,
or should we make sure it's sanitised?
Dave
> With regards,
> Daniel
> --
> |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org -o- https://fstop138.berrange.com :|
> |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
>
--
-----Open up your eyes, open up your mind, open up your code -------
/ Dr. David Alan Gilbert | Running GNU/Linux | Happy \
\ dave @ treblig.org | | In Hex /
\ _________________________|_____ http://www.treblig.org |_______/
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2] hw/uefi: add "info ovmf-log" + "query-ovmf-log" monitor commands
2025-10-09 11:41 ` Dr. David Alan Gilbert
@ 2025-10-09 12:00 ` Daniel P. Berrangé
2025-10-09 12:41 ` Gerd Hoffmann
0 siblings, 1 reply; 10+ messages in thread
From: Daniel P. Berrangé @ 2025-10-09 12:00 UTC (permalink / raw)
To: Dr. David Alan Gilbert
Cc: Gerd Hoffmann, qemu-devel, Laurent Vivier, Marcel Apfelbaum,
Markus Armbruster, Philippe Mathieu-Daudé, Yanan Wang,
Paolo Bonzini, Fabiano Rosas, Eric Blake, Eduardo Habkost,
Zhao Liu
On Thu, Oct 09, 2025 at 11:41:04AM +0000, Dr. David Alan Gilbert wrote:
> * Daniel P. Berrangé (berrange@redhat.com) wrote:
> > On Thu, Oct 09, 2025 at 01:56:09AM +0000, Dr. David Alan Gilbert wrote:
> > > * Gerd Hoffmann (kraxel@redhat.com) wrote:
> > > > Starting with the edk2-stable202508 tag OVMF (and ArmVirt too) have
> > > > optional support for logging to a memory buffer. There is guest side
> > > > support -- for example in linux kernels v6.17+ -- to read that buffer.
> > > > But that might not helpful if your guest stops booting early enough that
> > > > guest tooling can not be used yet. So host side support to read that
> > > > log buffer is a useful thing to have.
> > > >
> > > > This patch implements both qmp and hmp monitor commands to read the
> > > > firmware log.
> > > >
> > > > Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> > >
> > > I'm OK with that, but I wonder if it would be better to have a command
> > > that wrote the buffer to a file rather than displaying it directly; I don't
> > > think we normally have anything else which outputs that much raw guest
> > > provided data directly.
> > > I assume when it goes wrong you end up with random unprintable junk in
> > > the buffer.
> >
> > 128 KB is on the high side, but is not terrible. Libvirt (arbitrarily)
> > caps a QMP reply at 10 MB. Libvirt is going to want to send this on to
> > the client app and will likely do that streaming in memory, so having
> > it iin a file is not required from our POV.
> >
> > IIRC, some of the query-block command replies can get insanely huge
> > when the qcow2 chain is very long.
>
> OK, what about sanitisation - if that text contains random binary what happens,
> or should we make sure it's sanitised?
As prior art, the QGA 'guest-exec' command will return stdout/stderr
of the command in base64 format. The downside is that it is bloated
in size, but it is at least safe wrt JSON encoding. The HMP command
could still dump the raw data IMHO, as that's human facing and base64
is horrible for human consumption.
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2] hw/uefi: add "info ovmf-log" + "query-ovmf-log" monitor commands
2025-10-09 12:00 ` Daniel P. Berrangé
@ 2025-10-09 12:41 ` Gerd Hoffmann
2025-10-09 13:17 ` Dr. David Alan Gilbert
2025-10-09 14:06 ` Daniel P. Berrangé
0 siblings, 2 replies; 10+ messages in thread
From: Gerd Hoffmann @ 2025-10-09 12:41 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: Dr. David Alan Gilbert, qemu-devel, Laurent Vivier,
Marcel Apfelbaum, Markus Armbruster, Philippe Mathieu-Daudé,
Yanan Wang, Paolo Bonzini, Fabiano Rosas, Eric Blake,
Eduardo Habkost, Zhao Liu
Hi,
> > OK, what about sanitisation - if that text contains random binary what happens,
> > or should we make sure it's sanitised?
>
> As prior art, the QGA 'guest-exec' command will return stdout/stderr
> of the command in base64 format. The downside is that it is bloated
> in size, but it is at least safe wrt JSON encoding.
In theory the log should just be text, but I've managed to f*ck up
logging with broken patches in the past, with the result that random
binary crap landed in the log.
So sending base64 in the json reply makes sense to me. Do we have a
qapi type for that? Or should I use string?
> The HMP command could still dump the raw data IMHO, as that's human
> facing and base64 is horrible for human consumption.
And probably a hmp implementation /not/ using the qmp command so we
don't convert text -> base64 -> text ...
take care,
Gerd
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2] hw/uefi: add "info ovmf-log" + "query-ovmf-log" monitor commands
2025-10-09 12:41 ` Gerd Hoffmann
@ 2025-10-09 13:17 ` Dr. David Alan Gilbert
2025-10-09 14:06 ` Daniel P. Berrangé
1 sibling, 0 replies; 10+ messages in thread
From: Dr. David Alan Gilbert @ 2025-10-09 13:17 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: Daniel P. Berrangé, qemu-devel, Laurent Vivier,
Marcel Apfelbaum, Markus Armbruster, Philippe Mathieu-Daudé,
Yanan Wang, Paolo Bonzini, Fabiano Rosas, Eric Blake,
Eduardo Habkost, Zhao Liu
* Gerd Hoffmann (kraxel@redhat.com) wrote:
> Hi,
>
> > > OK, what about sanitisation - if that text contains random binary what happens,
> > > or should we make sure it's sanitised?
> >
> > As prior art, the QGA 'guest-exec' command will return stdout/stderr
> > of the command in base64 format. The downside is that it is bloated
> > in size, but it is at least safe wrt JSON encoding.
>
> In theory the log should just be text, but I've managed to f*ck up
> logging with broken patches in the past, with the result that random
> binary crap landed in the log.
>
> So sending base64 in the json reply makes sense to me. Do we have a
> qapi type for that? Or should I use string?
>
> > The HMP command could still dump the raw data IMHO, as that's human
> > facing and base64 is horrible for human consumption.
>
> And probably a hmp implementation /not/ using the qmp command so we
> don't convert text -> base64 -> text ...
Haha yes; if you can run the HMP through an 'isprint()' or similar
that would be good; I guess you want to keep cr/lf's and maybe tabs,
but you probably don't want anything that screw up the terminal?
Dave
> take care,
> Gerd
>
--
-----Open up your eyes, open up your mind, open up your code -------
/ Dr. David Alan Gilbert | Running GNU/Linux | Happy \
\ dave @ treblig.org | | In Hex /
\ _________________________|_____ http://www.treblig.org |_______/
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2] hw/uefi: add "info ovmf-log" + "query-ovmf-log" monitor commands
2025-10-09 12:41 ` Gerd Hoffmann
2025-10-09 13:17 ` Dr. David Alan Gilbert
@ 2025-10-09 14:06 ` Daniel P. Berrangé
1 sibling, 0 replies; 10+ messages in thread
From: Daniel P. Berrangé @ 2025-10-09 14:06 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: Dr. David Alan Gilbert, qemu-devel, Laurent Vivier,
Marcel Apfelbaum, Markus Armbruster, Philippe Mathieu-Daudé,
Yanan Wang, Paolo Bonzini, Fabiano Rosas, Eric Blake,
Eduardo Habkost, Zhao Liu
On Thu, Oct 09, 2025 at 02:41:51PM +0200, Gerd Hoffmann wrote:
> Hi,
>
> > > OK, what about sanitisation - if that text contains random binary what happens,
> > > or should we make sure it's sanitised?
> >
> > As prior art, the QGA 'guest-exec' command will return stdout/stderr
> > of the command in base64 format. The downside is that it is bloated
> > in size, but it is at least safe wrt JSON encoding.
>
> In theory the log should just be text, but I've managed to f*ck up
> logging with broken patches in the past, with the result that random
> binary crap landed in the log.
>
> So sending base64 in the json reply makes sense to me. Do we have a
> qapi type for that? Or should I use string?
>
> > The HMP command could still dump the raw data IMHO, as that's human
> > facing and base64 is horrible for human consumption.
>
> And probably a hmp implementation /not/ using the qmp command so we
> don't convert text -> base64 -> text ...
Although that is indeed inefficient, our overall long term goal is for
*all* HMP comamnds to be implemented by invoking a QMP command. This
will ultimately get us to the point where QAPI describes our public
facing functionality, and HMP could be moved to become an out-of-process
client side interface to QMP.
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2025-10-09 14:08 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-07 13:52 [PATCH v2] hw/uefi: add "info ovmf-log" + "query-ovmf-log" monitor commands Gerd Hoffmann
2025-10-08 6:32 ` Markus Armbruster
2025-10-08 7:09 ` Daniel P. Berrangé
2025-10-09 1:56 ` Dr. David Alan Gilbert
2025-10-09 7:13 ` Daniel P. Berrangé
2025-10-09 11:41 ` Dr. David Alan Gilbert
2025-10-09 12:00 ` Daniel P. Berrangé
2025-10-09 12:41 ` Gerd Hoffmann
2025-10-09 13:17 ` Dr. David Alan Gilbert
2025-10-09 14:06 ` Daniel P. Berrangé
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).