Add a new 'info ramblock-attributes' HMP command and the corresponding
'x-query-ramblock-attributes' QMP command to display the shared/private
memory attributes for ram blocks.
The QMP command returns structured data (RamBlockAttributesInfo list
with per-range shared/populated attributes), while HMP formats it for
human consumption.
This is useful for debugging confidential guests (TDX, SNP) to inspect
which memory regions are shared vs private, and their population state
when a RamDiscardManager is present (e.g. virtio-mem).
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
qapi/machine.json | 56 ++++++++++++++++++++++++++++++++++
include/monitor/hmp.h | 1 +
hw/core/machine-hmp-cmds.c | 32 +++++++++++++++++++
system/ram-block-attributes.c | 71 +++++++++++++++++++++++++++++++++++++++++++
hmp-commands-info.hx | 13 ++++++++
5 files changed, 173 insertions(+)
diff --git a/qapi/machine.json b/qapi/machine.json
index 685e4e29b87..a1af5c61176 100644
--- a/qapi/machine.json
+++ b/qapi/machine.json
@@ -1738,6 +1738,62 @@
'returns': 'HumanReadableText',
'features': [ 'unstable' ] }
+##
+# @RamBlockAttributeRange:
+#
+# A contiguous range within a ram block with uniform attributes.
+#
+# @start: start offset in bytes within the ram block
+#
+# @length: length in bytes of the range
+#
+# @shared: true if the range is shared, false if private
+#
+# @populated: true if the entire range is fully populated across all
+# RamDiscardManager sources; false if any sub-block is discarded.
+# Only present when a RamDiscardManager is managing the block.
+#
+# Since: 11.1
+##
+{ 'struct': 'RamBlockAttributeRange',
+ 'data': { 'start': 'uint64',
+ 'length': 'uint64',
+ 'shared': 'bool',
+ '*populated': 'bool' } }
+
+##
+# @RamBlockAttributesInfo:
+#
+# Shared/private memory attributes for a ram block.
+#
+# @name: the ram block identifier
+#
+# @ranges: list of attribute ranges
+#
+# Since: 11.1
+##
+{ 'struct': 'RamBlockAttributesInfo',
+ 'data': { 'name': 'str',
+ 'ranges': [ 'RamBlockAttributeRange' ] } }
+
+##
+# @x-query-ramblock-attributes:
+#
+# Query ram block shared/private attributes. This is useful
+# to debug confidential guests.
+#
+# Features:
+#
+# @unstable: This command is meant for debugging.
+#
+# Returns: list of ram block attributes
+#
+# Since: 11.1
+##
+{ 'command': 'x-query-ramblock-attributes',
+ 'returns': [ 'RamBlockAttributesInfo' ],
+ 'features': [ 'unstable' ] }
+
##
# @x-query-roms:
#
diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h
index 9258a049bff..c069d5de8d1 100644
--- a/include/monitor/hmp.h
+++ b/include/monitor/hmp.h
@@ -137,6 +137,7 @@ void hmp_info_dump(Monitor *mon, const QDict *qdict);
void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict);
void hmp_info_vm_generation_id(Monitor *mon, const QDict *qdict);
void hmp_info_memory_size_summary(Monitor *mon, const QDict *qdict);
+void hmp_info_ramblock_attributes(Monitor *mon, const QDict *qdict);
void hmp_info_replay(Monitor *mon, const QDict *qdict);
void hmp_replay_break(Monitor *mon, const QDict *qdict);
void hmp_replay_delete_break(Monitor *mon, const QDict *qdict);
diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c
index 46846f741a2..122e1a0f735 100644
--- a/hw/core/machine-hmp-cmds.c
+++ b/hw/core/machine-hmp-cmds.c
@@ -24,6 +24,7 @@
#include "qapi/string-output-visitor.h"
#include "qemu/error-report.h"
#include "system/numa.h"
+#include "system/ramblock.h"
#include "hw/core/boards.h"
void hmp_info_cpus(Monitor *mon, const QDict *qdict)
@@ -388,3 +389,34 @@ void hmp_info_memory_size_summary(Monitor *mon, const QDict *qdict)
}
hmp_handle_error(mon, err);
}
+
+void hmp_info_ramblock_attributes(Monitor *mon, const QDict *qdict)
+{
+ Error *err = NULL;
+ g_autoptr(RamBlockAttributesInfoList) list = NULL;
+ RamBlockAttributesInfoList *it;
+
+ list = qmp_x_query_ramblock_attributes(&err);
+ if (hmp_handle_error(mon, err)) {
+ return;
+ }
+
+ for (it = list; it; it = it->next) {
+ RamBlockAttributesInfo *rba = it->value;
+ RamBlockAttributeRangeList *r;
+
+ monitor_printf(mon, "%s:\n", rba->name);
+ for (r = rba->ranges; r; r = r->next) {
+ RamBlockAttributeRange *range = r->value;
+ const char *shared = range->shared ? "shared" : "private";
+ const char *pop = range->has_populated ?
+ (range->populated ? "+populated" : "-populated") : "";
+
+ monitor_printf(mon,
+ " 0x%016" PRIx64 "-0x%016" PRIx64 " %s%s\n",
+ range->start,
+ range->start + range->length - 1,
+ shared, pop);
+ }
+ }
+}
diff --git a/system/ram-block-attributes.c b/system/ram-block-attributes.c
index 59ec7a28eb0..fb657ffca35 100644
--- a/system/ram-block-attributes.c
+++ b/system/ram-block-attributes.c
@@ -11,6 +11,7 @@
#include "qemu/osdep.h"
#include "qemu/error-report.h"
+#include "qapi/qapi-commands-machine.h"
#include "system/ramblock.h"
#include "trace.h"
@@ -221,3 +222,73 @@ static void ram_block_attributes_class_init(ObjectClass *klass,
rdsc->get_min_granularity = ram_block_attributes_rds_get_min_granularity;
rdsc->is_populated = ram_block_attributes_rds_is_populated;
}
+
+RamBlockAttributesInfoList *qmp_x_query_ramblock_attributes(Error **errp)
+{
+ RamBlockAttributesInfoList *head = NULL, **tail = &head;
+ RAMBlock *block;
+ size_t rba_block_size = ram_block_attributes_get_block_size();
+
+ RCU_READ_LOCK_GUARD();
+
+ RAMBLOCK_FOREACH(block) {
+ RamBlockAttributesInfo *rba;
+ RamBlockAttributeRangeList **range_tail;
+ RamBlockAttributes *attr = block->attributes;
+ RamDiscardManager *rdm;
+ unsigned long pos;
+
+ if (!attr) {
+ continue;
+ }
+
+ rdm = memory_region_get_ram_discard_manager(block->mr);
+ g_assert(rdm);
+
+ rba = g_new0(RamBlockAttributesInfo, 1);
+ rba->name = g_strdup(block->idstr);
+ range_tail = &rba->ranges;
+
+ pos = 0;
+ while (pos < attr->bitmap_size) {
+ bool is_shared = test_bit(pos, attr->bitmap);
+ unsigned long next;
+ uint64_t start_offset, length;
+ RamBlockAttributeRange *range;
+
+ if (is_shared) {
+ next = find_next_zero_bit(attr->bitmap,
+ attr->bitmap_size, pos);
+ } else {
+ next = find_next_bit(attr->bitmap,
+ attr->bitmap_size, pos);
+ }
+
+ start_offset = (uint64_t)pos * rba_block_size;
+ length = (uint64_t)(next - pos) * rba_block_size;
+
+ range = g_new0(RamBlockAttributeRange, 1);
+ range->start = start_offset;
+ range->length = length;
+ range->shared = is_shared;
+
+ {
+ MemoryRegionSection section = {
+ .mr = block->mr,
+ .offset_within_region = start_offset,
+ .size = int128_make64(length),
+ };
+ range->has_populated = true;
+ range->populated =
+ ram_discard_manager_is_populated(rdm, §ion);
+ }
+
+ QAPI_LIST_APPEND(range_tail, range);
+ pos = next;
+ }
+
+ QAPI_LIST_APPEND(tail, rba);
+ }
+
+ return head;
+}
diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
index 82134eb6c21..f119b9cfebc 100644
--- a/hmp-commands-info.hx
+++ b/hmp-commands-info.hx
@@ -761,6 +761,19 @@ SRST
Dump all the ramblocks of the system.
ERST
+ {
+ .name = "ramblock-attributes",
+ .args_type = "",
+ .params = "",
+ .help = "Display ramblock shared/private attributes",
+ .cmd = hmp_info_ramblock_attributes,
+ },
+
+SRST
+ ``info ramblock-attributes``
+ Display the shared/private memory attributes for ram blocks.
+ERST
+
{
.name = "hotpluggable-cpus",
.args_type = "",
--
2.54.0
Marc-André Lureau <marcandre.lureau@redhat.com> writes:
> Add a new 'info ramblock-attributes' HMP command and the corresponding
> 'x-query-ramblock-attributes' QMP command to display the shared/private
> memory attributes for ram blocks.
>
> The QMP command returns structured data (RamBlockAttributesInfo list
> with per-range shared/populated attributes), while HMP formats it for
> human consumption.
>
> This is useful for debugging confidential guests (TDX, SNP) to inspect
> which memory regions are shared vs private, and their population state
> when a RamDiscardManager is present (e.g. virtio-mem).
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
> qapi/machine.json | 56 ++++++++++++++++++++++++++++++++++
> include/monitor/hmp.h | 1 +
> hw/core/machine-hmp-cmds.c | 32 +++++++++++++++++++
> system/ram-block-attributes.c | 71 +++++++++++++++++++++++++++++++++++++++++++
> hmp-commands-info.hx | 13 ++++++++
> 5 files changed, 173 insertions(+)
>
> diff --git a/qapi/machine.json b/qapi/machine.json
> index 685e4e29b87..a1af5c61176 100644
> --- a/qapi/machine.json
> +++ b/qapi/machine.json
> @@ -1738,6 +1738,62 @@
> 'returns': 'HumanReadableText',
> 'features': [ 'unstable' ] }
>
> +##
> +# @RamBlockAttributeRange:
> +#
> +# A contiguous range within a ram block with uniform attributes.
RAM please. More of the same below, not flagging it again.
> +#
> +# @start: start offset in bytes within the ram block
> +#
> +# @length: length in bytes of the range
> +#
> +# @shared: true if the range is shared, false if private
Can we assume that anybody with a use for x-query-ramblock-attributes
understands what "range is shared" means?
> +#
> +# @populated: true if the entire range is fully populated across all
> +# RamDiscardManager sources; false if any sub-block is discarded.
> +# Only present when a RamDiscardManager is managing the block.
Can we assume that anybody with a use for x-query-ramblock-attributes
understands what "a RamDiscardManager is managing the block" means?
I can't find anything about RamDiscardManager in docs. Would it make
sense to have something there, so we can point to it here?
> +#
> +# Since: 11.1
> +##
> +{ 'struct': 'RamBlockAttributeRange',
> + 'data': { 'start': 'uint64',
> + 'length': 'uint64',
> + 'shared': 'bool',
> + '*populated': 'bool' } }
> +
> +##
> +# @RamBlockAttributesInfo:
> +#
> +# Shared/private memory attributes for a ram block.
> +#
> +# @name: the ram block identifier
Apparently, this is a memory region name, obtained with
memory_region_name(). Correct?
If yes, then what about "@name: the memory region name"? Or maybe "the
name of the RAM block's memory region"?
> +#
> +# @ranges: list of attribute ranges
> +#
> +# Since: 11.1
> +##
> +{ 'struct': 'RamBlockAttributesInfo',
> + 'data': { 'name': 'str',
> + 'ranges': [ 'RamBlockAttributeRange' ] } }
> +
> +##
> +# @x-query-ramblock-attributes:
> +#
> +# Query ram block shared/private attributes. This is useful
> +# to debug confidential guests.
Ignorant question: how are RAM blocks related to memory regions?
> +#
> +# Features:
> +#
> +# @unstable: This command is meant for debugging.
> +#
> +# Returns: list of ram block attributes
> +#
> +# Since: 11.1
> +##
> +{ 'command': 'x-query-ramblock-attributes',
> + 'returns': [ 'RamBlockAttributesInfo' ],
> + 'features': [ 'unstable' ] }
> +
> ##
> # @x-query-roms:
> #
[...]
* Marc-André Lureau (marcandre.lureau@redhat.com) wrote:
> Add a new 'info ramblock-attributes' HMP command and the corresponding
> 'x-query-ramblock-attributes' QMP command to display the shared/private
> memory attributes for ram blocks.
>
> The QMP command returns structured data (RamBlockAttributesInfo list
> with per-range shared/populated attributes), while HMP formats it for
> human consumption.
>
> This is useful for debugging confidential guests (TDX, SNP) to inspect
> which memory regions are shared vs private, and their population state
> when a RamDiscardManager is present (e.g. virtio-mem).
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
> qapi/machine.json | 56 ++++++++++++++++++++++++++++++++++
> include/monitor/hmp.h | 1 +
> hw/core/machine-hmp-cmds.c | 32 +++++++++++++++++++
> system/ram-block-attributes.c | 71 +++++++++++++++++++++++++++++++++++++++++++
> hmp-commands-info.hx | 13 ++++++++
> 5 files changed, 173 insertions(+)
>
<snip>
For HMP,
Acked-by: Dr. David Alan Gilbert <dave@treblig.org>
however, you might consider whether it would be better to add it
to the existing "info ramblock"
Dave
> +void hmp_info_ramblock_attributes(Monitor *mon, const QDict *qdict)
> +{
> + Error *err = NULL;
> + g_autoptr(RamBlockAttributesInfoList) list = NULL;
> + RamBlockAttributesInfoList *it;
> +
> + list = qmp_x_query_ramblock_attributes(&err);
> + if (hmp_handle_error(mon, err)) {
> + return;
> + }
> +
> + for (it = list; it; it = it->next) {
> + RamBlockAttributesInfo *rba = it->value;
> + RamBlockAttributeRangeList *r;
> +
> + monitor_printf(mon, "%s:\n", rba->name);
> + for (r = rba->ranges; r; r = r->next) {
> + RamBlockAttributeRange *range = r->value;
> + const char *shared = range->shared ? "shared" : "private";
> + const char *pop = range->has_populated ?
> + (range->populated ? "+populated" : "-populated") : "";
> +
> + monitor_printf(mon,
> + " 0x%016" PRIx64 "-0x%016" PRIx64 " %s%s\n",
> + range->start,
> + range->start + range->length - 1,
> + shared, pop);
> + }
> + }
> +}
> diff --git a/system/ram-block-attributes.c b/system/ram-block-attributes.c
> index 59ec7a28eb0..fb657ffca35 100644
> --- a/system/ram-block-attributes.c
> +++ b/system/ram-block-attributes.c
> @@ -11,6 +11,7 @@
>
> #include "qemu/osdep.h"
> #include "qemu/error-report.h"
> +#include "qapi/qapi-commands-machine.h"
> #include "system/ramblock.h"
> #include "trace.h"
>
> @@ -221,3 +222,73 @@ static void ram_block_attributes_class_init(ObjectClass *klass,
> rdsc->get_min_granularity = ram_block_attributes_rds_get_min_granularity;
> rdsc->is_populated = ram_block_attributes_rds_is_populated;
> }
> +
> +RamBlockAttributesInfoList *qmp_x_query_ramblock_attributes(Error **errp)
> +{
> + RamBlockAttributesInfoList *head = NULL, **tail = &head;
> + RAMBlock *block;
> + size_t rba_block_size = ram_block_attributes_get_block_size();
> +
> + RCU_READ_LOCK_GUARD();
> +
> + RAMBLOCK_FOREACH(block) {
> + RamBlockAttributesInfo *rba;
> + RamBlockAttributeRangeList **range_tail;
> + RamBlockAttributes *attr = block->attributes;
> + RamDiscardManager *rdm;
> + unsigned long pos;
> +
> + if (!attr) {
> + continue;
> + }
> +
> + rdm = memory_region_get_ram_discard_manager(block->mr);
> + g_assert(rdm);
> +
> + rba = g_new0(RamBlockAttributesInfo, 1);
> + rba->name = g_strdup(block->idstr);
> + range_tail = &rba->ranges;
> +
> + pos = 0;
> + while (pos < attr->bitmap_size) {
> + bool is_shared = test_bit(pos, attr->bitmap);
> + unsigned long next;
> + uint64_t start_offset, length;
> + RamBlockAttributeRange *range;
> +
> + if (is_shared) {
> + next = find_next_zero_bit(attr->bitmap,
> + attr->bitmap_size, pos);
> + } else {
> + next = find_next_bit(attr->bitmap,
> + attr->bitmap_size, pos);
> + }
> +
> + start_offset = (uint64_t)pos * rba_block_size;
> + length = (uint64_t)(next - pos) * rba_block_size;
> +
> + range = g_new0(RamBlockAttributeRange, 1);
> + range->start = start_offset;
> + range->length = length;
> + range->shared = is_shared;
> +
> + {
> + MemoryRegionSection section = {
> + .mr = block->mr,
> + .offset_within_region = start_offset,
> + .size = int128_make64(length),
> + };
> + range->has_populated = true;
> + range->populated =
> + ram_discard_manager_is_populated(rdm, §ion);
> + }
> +
> + QAPI_LIST_APPEND(range_tail, range);
> + pos = next;
> + }
> +
> + QAPI_LIST_APPEND(tail, rba);
> + }
> +
> + return head;
> +}
> diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
> index 82134eb6c21..f119b9cfebc 100644
> --- a/hmp-commands-info.hx
> +++ b/hmp-commands-info.hx
> @@ -761,6 +761,19 @@ SRST
> Dump all the ramblocks of the system.
> ERST
>
> + {
> + .name = "ramblock-attributes",
> + .args_type = "",
> + .params = "",
> + .help = "Display ramblock shared/private attributes",
> + .cmd = hmp_info_ramblock_attributes,
> + },
> +
> +SRST
> + ``info ramblock-attributes``
> + Display the shared/private memory attributes for ram blocks.
> +ERST
> +
> {
> .name = "hotpluggable-cpus",
> .args_type = "",
>
> --
> 2.54.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 |_______/
"Dr. David Alan Gilbert" <dave@treblig.org> writes: > * Marc-André Lureau (marcandre.lureau@redhat.com) wrote: >> Add a new 'info ramblock-attributes' HMP command and the corresponding >> 'x-query-ramblock-attributes' QMP command to display the shared/private >> memory attributes for ram blocks. >> >> The QMP command returns structured data (RamBlockAttributesInfo list >> with per-range shared/populated attributes), while HMP formats it for >> human consumption. >> >> This is useful for debugging confidential guests (TDX, SNP) to inspect >> which memory regions are shared vs private, and their population state >> when a RamDiscardManager is present (e.g. virtio-mem). >> >> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> >> --- >> qapi/machine.json | 56 ++++++++++++++++++++++++++++++++++ >> include/monitor/hmp.h | 1 + >> hw/core/machine-hmp-cmds.c | 32 +++++++++++++++++++ >> system/ram-block-attributes.c | 71 +++++++++++++++++++++++++++++++++++++++++++ >> hmp-commands-info.hx | 13 ++++++++ >> 5 files changed, 173 insertions(+) >> > > <snip> > > For HMP, > > Acked-by: Dr. David Alan Gilbert <dave@treblig.org> > > however, you might consider whether it would be better to add it > to the existing "info ramblock" Valid suggestion. HMP "info ramblock" wraps around QMP x-query-ramblock, which returns HumanReadableText. HMP "info ramblock-attributes" wraps around QMP x-query-ramblock-attributes, which returns a list of RamBlockAttributesInfo. So, basic x-query-ramblock returns unstructured text, and x-query-ramblock-attributes returns structured data. Hmm. Feels a bit odd. [...]
© 2016 - 2026 Red Hat, Inc.