* [RFC v5 0/5] tests/qtest: Add memory-access attributes (secure/space)
@ 2026-08-14 3:56 Tao Tang
2026-08-14 3:56 ` [RFC v5 1/5] tests/qtest: Add attrs support to qtest server memory commands Tao Tang
` (4 more replies)
0 siblings, 5 replies; 9+ messages in thread
From: Tao Tang @ 2026-08-14 3:56 UTC (permalink / raw)
To: Fabiano Rosas, Laurent Vivier, Paolo Bonzini
Cc: qemu-devel, qemu-arm, Peter Maydell, Chen Baozi, Chao Liu,
Jim MacArthur, Tao Tang
This is v5 of the qtest memory-access attributes series. Thanks to everyone who
reviewed v4 and provided feedback.
Since v4:
- server-side attrs handling has been simplified
- raw response handling has been split into a preparatory patch, and reusable
libqtest assertions and typed failure helpers have been added
- raw commands are now used only for malformed protocol forms.
This series is also available in a branch below:
https://gitlab.com/TaoTang/qemu/tree/qtest-secure-v5
CI is all green:
https://gitlab.com/TaoTang/qemu/-/pipelines/2757894685
Motivation
----------
IOMMU functional testing is always stack-heavy: for example, even for the
Non-secure Arm SMMU model, end-to-end validation can require a fairly involved
software stack. To keep the testing loop tight and reproducible, I previously
proposed using iommu-testdev + QTest to validate IOMMU/SMMU behaviour without
booting a guest [1].
[1] https://www.qemu.org/docs/master/specs/iommu-testdev.html
However, the current QTest interface cannot tag memory accesses with MemTxAttrs,
so it is hard to write targeted tests for security-sensitive paths. This becomes
a bigger issue for Secure SMMU work, which is RFCing in [2], and for future Arm
RME-DA enablement.
[2] https://lore.kernel.org/qemu-devel/20260813161515.2788900-1-tangtao1634@phytium.com.cn/
This series therefore extends qtest memory access commands with an optional
attrs argument, adds matching libqtest helpers, and introduces tests that
exercise the new APIs against address spaces that actually differ by security
context.
In particular:
- on Arm virt,secure=on, the test now uses the secure-only RAM window so the
requested attrs must select the correct AddressSpace
- on x86 q35,smm=on, the API series covers normal RAM, and an extra NOT-MERGE
validation patch adds an SMM-only RAM window so the secure path can be
cross-checked against a location not visible from the default address space
Compatibility
-------------
This series is additive for existing qtest users:
- Existing qtest memory commands keep their current forms; attrs is an optional
extra argument
- Existing libqtest memory helpers remain available and are kept as wrappers
passing NULL attrs
- The first 4 patches are the upstreamable API and test additions
- The 5th patch is explicitly marked NOT-MERGE and only provides extra x86
RFC coverage by wiring in a q35 test-only SMM RAM window
One behavioural improvement is that memory access failures on the new paths are
now reported consistently as ERR, which lets tests validate address-space
selection failures directly.
Testing
-------
The API series was exercised with:
meson test -C build \
"qtest-aarch64/qtest-attrs-test" \
"qtest-i386/qtest-attrs-test" \
"qtest-x86_64/qtest-attrs-test"
The extra x86 cross-check from the NOT-MERGE patch was exercised with:
meson test -C build \
"qtest-i386/qtest-x86-attrs-test" \
"qtest-x86_64/qtest-x86-attrs-test"
Future work
-----------
Once the Secure SMMU series lands, I plan to build on this qtest API to extend
iommu-testdev-based tests to cover more security contexts while still avoiding
a full guest software stack. There is an experimental branch that already uses
this API to validate secure/non-secure SMMU behaviour:
https://gitlab.com/TaoTang/qemu/tree/integration/secure-smmu-v5-qtest
Tao Tang (5):
tests/qtest: Add attrs support to qtest server memory commands
tests/qtest: Add libqtest attrs helpers for memory accesses
tests/qtest: Add raw command response helper
tests/qtest: Add qtest-attrs-test for memory access attrs
[NOT-MERGE] tests/qtest: add q35 SMM-only x86 attrs coverage
hw/pci-host/q35.c | 27 ++
include/hw/pci-host/q35.h | 8 +
system/qtest.c | 349 +++++++++++++++++++++----
tests/qtest/libqtest-single.h | 156 +++++++++++
tests/qtest/libqtest.c | 402 +++++++++++++++++++++++++----
tests/qtest/libqtest.h | 288 +++++++++++++++++++++
tests/qtest/meson.build | 8 +-
tests/qtest/qtest-attrs-test.c | 305 ++++++++++++++++++++++
tests/qtest/qtest-x86-attrs-test.c | 170 ++++++++++++
9 files changed, 1600 insertions(+), 113 deletions(-)
create mode 100644 tests/qtest/qtest-attrs-test.c
create mode 100644 tests/qtest/qtest-x86-attrs-test.c
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [RFC v5 1/5] tests/qtest: Add attrs support to qtest server memory commands
2026-08-14 3:56 [RFC v5 0/5] tests/qtest: Add memory-access attributes (secure/space) Tao Tang
@ 2026-08-14 3:56 ` Tao Tang
2026-08-19 13:32 ` Fabiano Rosas
2026-08-14 3:56 ` [RFC v5 2/5] tests/qtest: Add libqtest attrs helpers for memory accesses Tao Tang
` (3 subsequent siblings)
4 siblings, 1 reply; 9+ messages in thread
From: Tao Tang @ 2026-08-14 3:56 UTC (permalink / raw)
To: Fabiano Rosas, Laurent Vivier, Paolo Bonzini
Cc: qemu-devel, qemu-arm, Peter Maydell, Chen Baozi, Chao Liu,
Jim MacArthur, Tao Tang
Extend qtest memory access commands to accept an optional attrs argument.
Supported attrs:
- secure (x86/Arm)
- space=non-secure|secure|root|realm (Arm-only)
For memory commands, parse attrs, select the corresponding AddressSpace
with cpu_asidx_from_attrs(), and issue accesses with the matching
MemTxAttrs.
Cover scalar and bulk memory commands, including:
- read{b,w,l,q} / write{b,w,l,q}
- read / write
- b64read / b64write
- memset
Also reject invalid attrs and excessive arguments, and document the new
command forms in qtest.c, and use qtest_send_memtx_error() to return
ERR for memory access failures.
Preserve legacy no-attrs semantics for compatibility: those commands
continue to ignore MemTxResult and return OK.
Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
---
system/qtest.c | 349 +++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 293 insertions(+), 56 deletions(-)
diff --git a/system/qtest.c b/system/qtest.c
index cf301239177..ac863779bdf 100644
--- a/system/qtest.c
+++ b/system/qtest.c
@@ -22,6 +22,7 @@
#include "hw/core/qdev.h"
#include "hw/core/irq.h"
#include "hw/core/cpu.h"
+#include "hw/arm/arm-security.h"
#include "qemu/accel.h"
#include "system/cpu-timers.h"
#include "qemu/config-file.h"
@@ -115,6 +116,14 @@ static void *qtest_server_send_opaque;
* PIO and memory access:
* """"""""""""""""""""""
*
+ * Memory access commands allow specifying memory transaction attributes,
+ * which is useful for testing devices that behave differently based on
+ * security state (e.g., Arm TrustZone/CCA or System Management Mode in x86).
+ *
+ * Only the following attributes are supported by the qtest server:
+ * ``secure`` sets MemTxAttrs.secure=1 (x86/Arm).
+ * ``space=...`` is Arm-specific and accepts: non-secure, secure, root, realm.
+ *
* .. code-block:: none
*
* > outb ADDR VALUE
@@ -210,6 +219,36 @@ static void *qtest_server_send_opaque;
* > memset ADDR SIZE VALUE
* < OK
*
+ * Memory commands accept an optional ATTRS argument after their existing
+ * operands:
+ *
+ * .. code-block:: none
+ *
+ * > read{b,w,l,q} ADDR [ATTRS]
+ * > write{b,w,l,q} ADDR VALUE [ATTRS]
+ * > read ADDR SIZE [ATTRS]
+ * > write ADDR SIZE DATA [ATTRS]
+ * > b64read ADDR SIZE [ATTRS]
+ * > b64write ADDR SIZE B64_DATA [ATTRS]
+ * > memset ADDR SIZE VALUE [ATTRS]
+ *
+ * Representative examples:
+ *
+ * .. code-block:: none
+ *
+ * > readl ADDR secure
+ * < OK VALUE
+ *
+ * .. code-block:: none
+ *
+ * > b64read ADDR SIZE space=root
+ * < OK B64_DATA
+ *
+ * .. code-block:: none
+ *
+ * > writeq ADDR VALUE space=realm
+ * < OK
+ *
* ADDR, SIZE, VALUE are all integers parsed with strtoul() with a base of 0.
* For 'memset' a zero size is permitted and does nothing.
*
@@ -352,6 +391,141 @@ static void qtest_install_gpio_out_intercept(DeviceState *dev, const char *name,
*disconnected = qdev_intercept_gpio_out(dev, icpt, name, n);
}
+static bool qtest_parse_mem_attrs(CharFrontend *chr, const char *arg,
+ MemTxAttrs *attrs)
+{
+ if (!arg) {
+ *attrs = MEMTXATTRS_UNSPECIFIED;
+ return true;
+ }
+
+ if (strcmp(arg, "secure") == 0) {
+ *attrs = (MemTxAttrs){ .secure = 1 };
+ return true;
+ }
+
+ if (strncmp(arg, "space=", 6) == 0) {
+ const char *space = arg + 6;
+ ARMSecuritySpace sec_space;
+
+ if (!target_arm() && !target_aarch64()) {
+ qtest_send(chr, "ERR space=<...> is Arm-specific\n");
+ return false;
+ }
+
+ if (strcmp(space, "non-secure") == 0) {
+ sec_space = ARMSS_NonSecure;
+ } else if (strcmp(space, "secure") == 0) {
+ sec_space = ARMSS_Secure;
+ } else if (strcmp(space, "root") == 0) {
+ sec_space = ARMSS_Root;
+ } else if (strcmp(space, "realm") == 0) {
+ sec_space = ARMSS_Realm;
+ } else {
+ qtest_send(chr, "ERR invalid space value. Valid space: "
+ "secure/non-secure/root/realm\n");
+ return false;
+ }
+
+ *attrs = (MemTxAttrs){
+ .space = sec_space,
+ .secure = arm_space_is_secure(sec_space),
+ };
+ return true;
+ }
+
+ qtest_send(chr, "ERR invalid attrs argument\n");
+ return false;
+}
+
+static bool qtest_get_mem_as(CharFrontend *chr, MemTxAttrs attrs,
+ AddressSpace **as)
+{
+ int asidx;
+
+ /*
+ * cpu_asidx_from_attrs mainly uses attrs to call ->asidx_from_attrs. We use
+ * first_cpu as it's readily available.
+ */
+
+ asidx = cpu_asidx_from_attrs(first_cpu, attrs);
+ *as = cpu_get_address_space(first_cpu, asidx);
+ if (!*as) {
+ qtest_send(chr, "ERR address space unavailable for attrs\n");
+ return false;
+ }
+
+ return true;
+}
+
+static void qtest_send_memtx_error(CharFrontend *chr, MemTxResult result)
+{
+ qtest_sendf(chr, "ERR memory access failed 0x%x\n", result);
+}
+
+static MemTxResult qtest_write_sized(AddressSpace *as, uint64_t addr,
+ MemTxAttrs attrs, uint64_t value,
+ char size)
+{
+ switch (size) {
+ case 'b': {
+ uint8_t data = value;
+ return address_space_write(as, addr, attrs, &data, 1);
+ }
+ case 'w': {
+ uint16_t data = value;
+ tswap16s(&data);
+ return address_space_write(as, addr, attrs, &data, 2);
+ }
+ case 'l': {
+ uint32_t data = value;
+ tswap32s(&data);
+ return address_space_write(as, addr, attrs, &data, 4);
+ }
+ case 'q': {
+ uint64_t data = value;
+ tswap64s(&data);
+ return address_space_write(as, addr, attrs, &data, 8);
+ }
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static MemTxResult qtest_read_sized(AddressSpace *as, uint64_t addr,
+ MemTxAttrs attrs, char size,
+ uint64_t *value)
+{
+ switch (size) {
+ case 'b': {
+ uint8_t data = 0;
+ MemTxResult result = address_space_read(as, addr, attrs, &data, 1);
+ *value = data;
+ return result;
+ }
+ case 'w': {
+ uint16_t data = 0;
+ MemTxResult result = address_space_read(as, addr, attrs, &data, 2);
+ *value = tswap16(data);
+ return result;
+ }
+ case 'l': {
+ uint32_t data = 0;
+ MemTxResult result = address_space_read(as, addr, attrs, &data, 4);
+ *value = tswap32(data);
+ return result;
+ }
+ case 'q': {
+ uint64_t data = 0;
+ MemTxResult result = address_space_read(as, addr, attrs, &data, 8);
+ *value = tswap64(data);
+ return result;
+ }
+ default:
+ g_assert_not_reached();
+ }
+}
+
static void qtest_process_command(CharFrontend *chr, gchar **words)
{
const gchar *command;
@@ -509,33 +683,29 @@ static void qtest_process_command(CharFrontend *chr, gchar **words)
strcmp(words[0], "writeq") == 0) {
uint64_t addr;
uint64_t value;
+ MemTxResult result;
+ MemTxAttrs attrs;
+ AddressSpace *as;
int ret;
g_assert(words[1] && words[2]);
+ if (words[3] && words[4]) {
+ qtest_send(chr, "ERR too many arguments\n");
+ return;
+ }
ret = qemu_strtou64(words[1], NULL, 0, &addr);
g_assert(ret == 0);
ret = qemu_strtou64(words[2], NULL, 0, &value);
g_assert(ret == 0);
+ if (!qtest_parse_mem_attrs(chr, words[3], &attrs) ||
+ !qtest_get_mem_as(chr, attrs, &as)) {
+ return;
+ }
- if (words[0][5] == 'b') {
- uint8_t data = value;
- address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
- &data, 1);
- } else if (words[0][5] == 'w') {
- uint16_t data = value;
- tswap16s(&data);
- address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
- &data, 2);
- } else if (words[0][5] == 'l') {
- uint32_t data = value;
- tswap32s(&data);
- address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
- &data, 4);
- } else if (words[0][5] == 'q') {
- uint64_t data = value;
- tswap64s(&data);
- address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
- &data, 8);
+ result = qtest_write_sized(as, addr, attrs, value, words[0][5]);
+ if (words[3] && result != MEMTX_OK) {
+ qtest_send_memtx_error(chr, result);
+ return;
}
qtest_send(chr, "OK\n");
} else if (strcmp(words[0], "readb") == 0 ||
@@ -543,51 +713,62 @@ static void qtest_process_command(CharFrontend *chr, gchar **words)
strcmp(words[0], "readl") == 0 ||
strcmp(words[0], "readq") == 0) {
uint64_t addr;
- uint64_t value = UINT64_C(-1);
+ uint64_t value = 0;
+ MemTxResult result;
+ MemTxAttrs attrs;
+ AddressSpace *as;
int ret;
g_assert(words[1]);
+ if (words[2] && words[3]) {
+ qtest_send(chr, "ERR too many arguments\n");
+ return;
+ }
ret = qemu_strtou64(words[1], NULL, 0, &addr);
g_assert(ret == 0);
+ if (!qtest_parse_mem_attrs(chr, words[2], &attrs) ||
+ !qtest_get_mem_as(chr, attrs, &as)) {
+ return;
+ }
- if (words[0][4] == 'b') {
- uint8_t data;
- address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
- &data, 1);
- value = data;
- } else if (words[0][4] == 'w') {
- uint16_t data;
- address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
- &data, 2);
- value = tswap16(data);
- } else if (words[0][4] == 'l') {
- uint32_t data;
- address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
- &data, 4);
- value = tswap32(data);
- } else if (words[0][4] == 'q') {
- address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
- &value, 8);
- tswap64s(&value);
+ result = qtest_read_sized(as, addr, attrs, words[0][4], &value);
+ if (words[2] && result != MEMTX_OK) {
+ qtest_send_memtx_error(chr, result);
+ return;
}
qtest_sendf(chr, "OK 0x%016" PRIx64 "\n", value);
} else if (strcmp(words[0], "read") == 0) {
g_autoptr(GString) enc = NULL;
uint64_t addr, len;
uint8_t *data;
+ MemTxResult result;
+ MemTxAttrs attrs;
+ AddressSpace *as;
int ret;
g_assert(words[1] && words[2]);
+ if (words[3] && words[4]) {
+ qtest_send(chr, "ERR too many arguments\n");
+ return;
+ }
ret = qemu_strtou64(words[1], NULL, 0, &addr);
g_assert(ret == 0);
ret = qemu_strtou64(words[2], NULL, 0, &len);
g_assert(ret == 0);
/* We'd send garbage to libqtest if len is 0 */
g_assert(len);
+ if (!qtest_parse_mem_attrs(chr, words[3], &attrs) ||
+ !qtest_get_mem_as(chr, attrs, &as)) {
+ return;
+ }
- data = g_malloc(len);
- address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data,
- len);
+ data = g_malloc0(len);
+ result = address_space_read(as, addr, attrs, data, len);
+ if (words[3] && result != MEMTX_OK) {
+ g_free(data);
+ qtest_send_memtx_error(chr, result);
+ return;
+ }
enc = qemu_hexdump_line(NULL, data, len, 0, 0);
@@ -598,17 +779,32 @@ static void qtest_process_command(CharFrontend *chr, gchar **words)
uint64_t addr, len;
uint8_t *data;
gchar *b64_data;
+ MemTxResult result;
+ MemTxAttrs attrs;
+ AddressSpace *as;
int ret;
g_assert(words[1] && words[2]);
+ if (words[3] && words[4]) {
+ qtest_send(chr, "ERR too many arguments\n");
+ return;
+ }
ret = qemu_strtou64(words[1], NULL, 0, &addr);
g_assert(ret == 0);
ret = qemu_strtou64(words[2], NULL, 0, &len);
g_assert(ret == 0);
+ if (!qtest_parse_mem_attrs(chr, words[3], &attrs) ||
+ !qtest_get_mem_as(chr, attrs, &as)) {
+ return;
+ }
- data = g_malloc(len);
- address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data,
- len);
+ data = g_malloc0(len);
+ result = address_space_read(as, addr, attrs, data, len);
+ if (words[3] && result != MEMTX_OK) {
+ g_free(data);
+ qtest_send_memtx_error(chr, result);
+ return;
+ }
b64_data = g_base64_encode(data, len);
qtest_sendf(chr, "OK %s\n", b64_data);
@@ -618,14 +814,20 @@ static void qtest_process_command(CharFrontend *chr, gchar **words)
uint64_t addr, len, i;
uint8_t *data;
size_t data_len;
+ MemTxResult result;
+ MemTxAttrs attrs;
+ AddressSpace *as;
int ret;
g_assert(words[1] && words[2] && words[3]);
+ if (words[4] && words[5]) {
+ qtest_send(chr, "ERR too many arguments\n");
+ return;
+ }
ret = qemu_strtou64(words[1], NULL, 0, &addr);
g_assert(ret == 0);
ret = qemu_strtou64(words[2], NULL, 0, &len);
g_assert(ret == 0);
-
data_len = strlen(words[3]);
if (data_len < 3) {
qtest_send(chr, "ERR invalid argument size\n");
@@ -641,47 +843,75 @@ static void qtest_process_command(CharFrontend *chr, gchar **words)
data[i] = 0;
}
}
- address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data,
- len);
+ if (!qtest_parse_mem_attrs(chr, words[4], &attrs) ||
+ !qtest_get_mem_as(chr, attrs, &as)) {
+ g_free(data);
+ return;
+ }
+ result = address_space_write(as, addr, attrs, data, len);
g_free(data);
+ if (words[4] && result != MEMTX_OK) {
+ qtest_send_memtx_error(chr, result);
+ return;
+ }
qtest_send(chr, "OK\n");
} else if (strcmp(words[0], "memset") == 0) {
uint64_t addr, len;
- uint8_t *data;
unsigned long pattern;
+ MemTxResult result;
+ MemTxAttrs attrs;
+ AddressSpace *as;
int ret;
g_assert(words[1] && words[2] && words[3]);
+ if (words[4] && words[5]) {
+ qtest_send(chr, "ERR too many arguments\n");
+ return;
+ }
ret = qemu_strtou64(words[1], NULL, 0, &addr);
g_assert(ret == 0);
ret = qemu_strtou64(words[2], NULL, 0, &len);
g_assert(ret == 0);
ret = qemu_strtoul(words[3], NULL, 0, &pattern);
g_assert(ret == 0);
+ if (!qtest_parse_mem_attrs(chr, words[4], &attrs) ||
+ !qtest_get_mem_as(chr, attrs, &as)) {
+ return;
+ }
if (len) {
- data = g_malloc(len);
+ uint8_t *data = g_malloc(len);
+
memset(data, pattern, len);
- address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
- data, len);
+ result = address_space_write(as, addr, attrs, data, len);
g_free(data);
+ if (words[4] && result != MEMTX_OK) {
+ qtest_send_memtx_error(chr, result);
+ return;
+ }
}
qtest_send(chr, "OK\n");
- } else if (strcmp(words[0], "b64write") == 0) {
+ } else if (strcmp(words[0], "b64write") == 0) {
uint64_t addr, len;
uint8_t *data;
size_t data_len;
gsize out_len;
+ MemTxResult result;
+ MemTxAttrs attrs;
+ AddressSpace *as;
int ret;
g_assert(words[1] && words[2] && words[3]);
+ if (words[4] && words[5]) {
+ qtest_send(chr, "ERR too many arguments\n");
+ return;
+ }
ret = qemu_strtou64(words[1], NULL, 0, &addr);
g_assert(ret == 0);
ret = qemu_strtou64(words[2], NULL, 0, &len);
g_assert(ret == 0);
-
data_len = strlen(words[3]);
if (data_len < 3) {
qtest_send(chr, "ERR invalid argument size\n");
@@ -696,8 +926,15 @@ static void qtest_process_command(CharFrontend *chr, gchar **words)
out_len = MIN(out_len, len);
}
- address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data,
- out_len);
+ if (!qtest_parse_mem_attrs(chr, words[4], &attrs) ||
+ !qtest_get_mem_as(chr, attrs, &as)) {
+ return;
+ }
+ result = address_space_write(as, addr, attrs, data, out_len);
+ if (words[4] && result != MEMTX_OK) {
+ qtest_send_memtx_error(chr, result);
+ return;
+ }
qtest_send(chr, "OK\n");
} else if (strcmp(words[0], "endianness") == 0) {
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [RFC v5 2/5] tests/qtest: Add libqtest attrs helpers for memory accesses
2026-08-14 3:56 [RFC v5 0/5] tests/qtest: Add memory-access attributes (secure/space) Tao Tang
2026-08-14 3:56 ` [RFC v5 1/5] tests/qtest: Add attrs support to qtest server memory commands Tao Tang
@ 2026-08-14 3:56 ` Tao Tang
2026-08-14 3:56 ` [RFC v5 3/5] tests/qtest: Add raw command response helper Tao Tang
` (2 subsequent siblings)
4 siblings, 0 replies; 9+ messages in thread
From: Tao Tang @ 2026-08-14 3:56 UTC (permalink / raw)
To: Fabiano Rosas, Laurent Vivier, Paolo Bonzini
Cc: qemu-devel, qemu-arm, Peter Maydell, Chen Baozi, Chao Liu,
Jim MacArthur, Tao Tang
Expose libqtest APIs for qtest memory commands with optional attrs:
- qtest_{read,write}{b,w,l,q}_attrs()
- qtest_mem{read,write,set}_attrs()
- qtest_buf{read,write}_attrs()
Keep the existing libqtest memory access helpers as wrappers passing
NULL attrs, so current users remain unchanged.
Also add matching libqtest-single shortcut wrappers for the *_attrs
helpers.
Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com>
---
tests/qtest/libqtest-single.h | 156 ++++++++++++++++++++++++
tests/qtest/libqtest.c | 219 ++++++++++++++++++++++++++--------
tests/qtest/libqtest.h | 164 +++++++++++++++++++++++++
3 files changed, 486 insertions(+), 53 deletions(-)
diff --git a/tests/qtest/libqtest-single.h b/tests/qtest/libqtest-single.h
index 851724cbcb8..2812b86bd45 100644
--- a/tests/qtest/libqtest-single.h
+++ b/tests/qtest/libqtest-single.h
@@ -291,6 +291,162 @@ static inline void memwrite(uint64_t addr, const void *data, size_t size)
qtest_memwrite(global_qtest, addr, data, size);
}
+/**
+ * writeb_attrs:
+ * @addr: Guest address to write to.
+ * @value: Value being written.
+ * @attrs: Optional transaction attributes string.
+ *
+ * Writes an 8-bit value to guest memory with optional transaction attributes.
+ */
+static inline void writeb_attrs(uint64_t addr, uint8_t value, const char *attrs)
+{
+ qtest_writeb_attrs(global_qtest, addr, value, attrs);
+}
+
+/**
+ * writew_attrs:
+ * @addr: Guest address to write to.
+ * @value: Value being written.
+ * @attrs: Optional transaction attributes string.
+ *
+ * Writes a 16-bit value to guest memory with optional transaction attributes.
+ */
+static inline void writew_attrs(uint64_t addr, uint16_t value,
+ const char *attrs)
+{
+ qtest_writew_attrs(global_qtest, addr, value, attrs);
+}
+
+/**
+ * writel_attrs:
+ * @addr: Guest address to write to.
+ * @value: Value being written.
+ * @attrs: Optional transaction attributes string.
+ *
+ * Writes a 32-bit value to guest memory with optional transaction attributes.
+ */
+static inline void writel_attrs(uint64_t addr, uint32_t value,
+ const char *attrs)
+{
+ qtest_writel_attrs(global_qtest, addr, value, attrs);
+}
+
+/**
+ * writeq_attrs:
+ * @addr: Guest address to write to.
+ * @value: Value being written.
+ * @attrs: Optional transaction attributes string.
+ *
+ * Writes a 64-bit value to guest memory with optional transaction attributes.
+ */
+static inline void writeq_attrs(uint64_t addr, uint64_t value,
+ const char *attrs)
+{
+ qtest_writeq_attrs(global_qtest, addr, value, attrs);
+}
+
+/**
+ * readb_attrs:
+ * @addr: Guest address to read from.
+ * @attrs: Optional transaction attributes string.
+ *
+ * Reads an 8-bit value from guest memory with optional transaction attributes.
+ *
+ * Returns: Value read.
+ */
+static inline uint8_t readb_attrs(uint64_t addr, const char *attrs)
+{
+ return qtest_readb_attrs(global_qtest, addr, attrs);
+}
+
+/**
+ * readw_attrs:
+ * @addr: Guest address to read from.
+ * @attrs: Optional transaction attributes string.
+ *
+ * Reads a 16-bit value from guest memory with optional transaction attributes.
+ *
+ * Returns: Value read.
+ */
+static inline uint16_t readw_attrs(uint64_t addr, const char *attrs)
+{
+ return qtest_readw_attrs(global_qtest, addr, attrs);
+}
+
+/**
+ * readl_attrs:
+ * @addr: Guest address to read from.
+ * @attrs: Optional transaction attributes string.
+ *
+ * Reads a 32-bit value from guest memory with optional transaction attributes.
+ *
+ * Returns: Value read.
+ */
+static inline uint32_t readl_attrs(uint64_t addr, const char *attrs)
+{
+ return qtest_readl_attrs(global_qtest, addr, attrs);
+}
+
+/**
+ * readq_attrs:
+ * @addr: Guest address to read from.
+ * @attrs: Optional transaction attributes string.
+ *
+ * Reads a 64-bit value from guest memory with optional transaction attributes.
+ *
+ * Returns: Value read.
+ */
+static inline uint64_t readq_attrs(uint64_t addr, const char *attrs)
+{
+ return qtest_readq_attrs(global_qtest, addr, attrs);
+}
+
+/**
+ * memread_attrs:
+ * @addr: Guest address to read from.
+ * @data: Pointer to where memory contents will be stored.
+ * @size: Number of bytes to read.
+ * @attrs: Optional transaction attributes string.
+ *
+ * Read guest memory into a buffer with optional transaction attributes.
+ */
+static inline void memread_attrs(uint64_t addr, void *data, size_t size,
+ const char *attrs)
+{
+ qtest_memread_attrs(global_qtest, addr, data, size, attrs);
+}
+
+/**
+ * memwrite_attrs:
+ * @addr: Guest address to write to.
+ * @data: Pointer to the bytes that will be written to guest memory.
+ * @size: Number of bytes to write.
+ * @attrs: Optional transaction attributes string.
+ *
+ * Write a buffer to guest memory with optional transaction attributes.
+ */
+static inline void memwrite_attrs(uint64_t addr, const void *data, size_t size,
+ const char *attrs)
+{
+ qtest_memwrite_attrs(global_qtest, addr, data, size, attrs);
+}
+
+/**
+ * memset_attrs:
+ * @addr: Guest address to write to.
+ * @pattern: Byte pattern to fill the guest memory region with.
+ * @size: Number of bytes to write.
+ * @attrs: Optional transaction attributes string.
+ *
+ * Write a pattern to guest memory with optional transaction attributes.
+ */
+static inline void memset_attrs(uint64_t addr, uint8_t pattern, size_t size,
+ const char *attrs)
+{
+ qtest_memset_attrs(global_qtest, addr, pattern, size, attrs);
+}
+
/**
* clock_step_next:
*
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
index c33c799c92d..453d0dddd09 100644
--- a/tests/qtest/libqtest.c
+++ b/tests/qtest/libqtest.c
@@ -1254,66 +1254,44 @@ uint32_t qtest_inl(QTestState *s, uint16_t addr)
return qtest_big_endian(s) ? bswap32(v) : v;
}
-static void qtest_write(QTestState *s, const char *cmd, uint64_t addr,
- uint64_t value)
-{
- qtest_sendf(s, "%s 0x%" PRIx64 " 0x%" PRIx64 "\n", cmd, addr, value);
- qtest_rsp(s);
-}
-
void qtest_writeb(QTestState *s, uint64_t addr, uint8_t value)
{
- qtest_write(s, "writeb", addr, value);
+ qtest_writeb_attrs(s, addr, value, NULL);
}
void qtest_writew(QTestState *s, uint64_t addr, uint16_t value)
{
- qtest_write(s, "writew", addr, value);
+ qtest_writew_attrs(s, addr, value, NULL);
}
void qtest_writel(QTestState *s, uint64_t addr, uint32_t value)
{
- qtest_write(s, "writel", addr, value);
+ qtest_writel_attrs(s, addr, value, NULL);
}
void qtest_writeq(QTestState *s, uint64_t addr, uint64_t value)
{
- qtest_write(s, "writeq", addr, value);
-}
-
-static uint64_t qtest_read(QTestState *s, const char *cmd, uint64_t addr)
-{
- gchar **args;
- int ret;
- uint64_t value;
-
- qtest_sendf(s, "%s 0x%" PRIx64 "\n", cmd, addr);
- args = qtest_rsp_args(s, 2);
- ret = qemu_strtou64(args[1], NULL, 0, &value);
- g_assert(!ret);
- g_strfreev(args);
-
- return value;
+ qtest_writeq_attrs(s, addr, value, NULL);
}
uint8_t qtest_readb(QTestState *s, uint64_t addr)
{
- return qtest_read(s, "readb", addr);
+ return qtest_readb_attrs(s, addr, NULL);
}
uint16_t qtest_readw(QTestState *s, uint64_t addr)
{
- return qtest_read(s, "readw", addr);
+ return qtest_readw_attrs(s, addr, NULL);
}
uint32_t qtest_readl(QTestState *s, uint64_t addr)
{
- return qtest_read(s, "readl", addr);
+ return qtest_readl_attrs(s, addr, NULL);
}
uint64_t qtest_readq(QTestState *s, uint64_t addr)
{
- return qtest_read(s, "readq", addr);
+ return qtest_readq_attrs(s, addr, NULL);
}
static int hex2nib(char ch)
@@ -1331,23 +1309,7 @@ static int hex2nib(char ch)
void qtest_memread(QTestState *s, uint64_t addr, void *data, size_t size)
{
- uint8_t *ptr = data;
- gchar **args;
- size_t i;
-
- if (!size) {
- return;
- }
-
- qtest_sendf(s, "read 0x%" PRIx64 " 0x%zx\n", addr, size);
- args = qtest_rsp_args(s, 2);
-
- for (i = 0; i < size; i++) {
- ptr[i] = hex2nib(args[1][2 + (i * 2)]) << 4;
- ptr[i] |= hex2nib(args[1][2 + (i * 2) + 1]);
- }
-
- g_strfreev(args);
+ qtest_memread_attrs(s, addr, data, size, NULL);
}
uint64_t qtest_rtas_call(QTestState *s, const char *name,
@@ -1412,23 +1374,58 @@ void qtest_add_data_func(const char *str, const void *data,
}
void qtest_bufwrite(QTestState *s, uint64_t addr, const void *data, size_t size)
+{
+ qtest_bufwrite_attrs(s, addr, data, size, NULL);
+}
+
+void qtest_bufread(QTestState *s, uint64_t addr, void *data, size_t size)
+{
+ qtest_bufread_attrs(s, addr, data, size, NULL);
+}
+
+void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size)
+{
+ qtest_memwrite_attrs(s, addr, data, size, NULL);
+}
+
+void qtest_memset(QTestState *s, uint64_t addr, uint8_t pattern, size_t size)
+{
+ qtest_memset_attrs(s, addr, pattern, size, NULL);
+}
+
+static bool qtest_has_attrs(const char *attrs)
+{
+ return attrs && attrs[0];
+}
+
+void qtest_bufwrite_attrs(QTestState *s, uint64_t addr, const void *data,
+ size_t size, const char *attrs)
{
gchar *bdata;
bdata = g_base64_encode(data, size);
qtest_sendf(s, "b64write 0x%" PRIx64 " 0x%zx ", addr, size);
s->ops.send(s, bdata);
+ if (qtest_has_attrs(attrs)) {
+ s->ops.send(s, " ");
+ s->ops.send(s, attrs);
+ }
s->ops.send(s, "\n");
qtest_rsp(s);
g_free(bdata);
}
-void qtest_bufread(QTestState *s, uint64_t addr, void *data, size_t size)
+void qtest_bufread_attrs(QTestState *s, uint64_t addr, void *data, size_t size,
+ const char *attrs)
{
gchar **args;
size_t len;
- qtest_sendf(s, "b64read 0x%" PRIx64 " 0x%zx\n", addr, size);
+ if (qtest_has_attrs(attrs)) {
+ qtest_sendf(s, "b64read 0x%" PRIx64 " 0x%zx %s\n", addr, size, attrs);
+ } else {
+ qtest_sendf(s, "b64read 0x%" PRIx64 " 0x%zx\n", addr, size);
+ }
args = qtest_rsp_args(s, 2);
g_base64_decode_inplace(args[1], &len);
@@ -1442,7 +1439,111 @@ void qtest_bufread(QTestState *s, uint64_t addr, void *data, size_t size)
g_strfreev(args);
}
-void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size)
+static void qtest_write_attrs(QTestState *s, const char *cmd,
+ uint64_t addr, uint64_t value,
+ const char *attrs)
+{
+ if (qtest_has_attrs(attrs)) {
+ qtest_sendf(s, "%s 0x%" PRIx64 " 0x%" PRIx64 " %s\n",
+ cmd, addr, value, attrs);
+ } else {
+ qtest_sendf(s, "%s 0x%" PRIx64 " 0x%" PRIx64 "\n", cmd, addr, value);
+ }
+ qtest_rsp(s);
+}
+
+static uint64_t qtest_read_attrs(QTestState *s, const char *cmd,
+ uint64_t addr, const char *attrs)
+{
+ gchar **args;
+ int ret;
+ uint64_t value;
+
+ if (qtest_has_attrs(attrs)) {
+ qtest_sendf(s, "%s 0x%" PRIx64 " %s\n", cmd, addr, attrs);
+ } else {
+ qtest_sendf(s, "%s 0x%" PRIx64 "\n", cmd, addr);
+ }
+ args = qtest_rsp_args(s, 2);
+ ret = qemu_strtou64(args[1], NULL, 0, &value);
+ g_assert(!ret);
+ g_strfreev(args);
+
+ return value;
+}
+
+void qtest_writeb_attrs(QTestState *s, uint64_t addr, uint8_t value,
+ const char *attrs)
+{
+ qtest_write_attrs(s, "writeb", addr, value, attrs);
+}
+
+void qtest_writew_attrs(QTestState *s, uint64_t addr, uint16_t value,
+ const char *attrs)
+{
+ qtest_write_attrs(s, "writew", addr, value, attrs);
+}
+
+void qtest_writel_attrs(QTestState *s, uint64_t addr, uint32_t value,
+ const char *attrs)
+{
+ qtest_write_attrs(s, "writel", addr, value, attrs);
+}
+
+void qtest_writeq_attrs(QTestState *s, uint64_t addr, uint64_t value,
+ const char *attrs)
+{
+ qtest_write_attrs(s, "writeq", addr, value, attrs);
+}
+
+uint8_t qtest_readb_attrs(QTestState *s, uint64_t addr, const char *attrs)
+{
+ return qtest_read_attrs(s, "readb", addr, attrs);
+}
+
+uint16_t qtest_readw_attrs(QTestState *s, uint64_t addr, const char *attrs)
+{
+ return qtest_read_attrs(s, "readw", addr, attrs);
+}
+
+uint32_t qtest_readl_attrs(QTestState *s, uint64_t addr, const char *attrs)
+{
+ return qtest_read_attrs(s, "readl", addr, attrs);
+}
+
+uint64_t qtest_readq_attrs(QTestState *s, uint64_t addr, const char *attrs)
+{
+ return qtest_read_attrs(s, "readq", addr, attrs);
+}
+
+void qtest_memread_attrs(QTestState *s, uint64_t addr, void *data,
+ size_t size, const char *attrs)
+{
+ uint8_t *ptr = data;
+ gchar **args;
+ size_t i;
+
+ if (!size) {
+ return;
+ }
+
+ if (qtest_has_attrs(attrs)) {
+ qtest_sendf(s, "read 0x%" PRIx64 " 0x%zx %s\n", addr, size, attrs);
+ } else {
+ qtest_sendf(s, "read 0x%" PRIx64 " 0x%zx\n", addr, size);
+ }
+ args = qtest_rsp_args(s, 2);
+
+ for (i = 0; i < size; i++) {
+ ptr[i] = hex2nib(args[1][2 + (i * 2)]) << 4;
+ ptr[i] |= hex2nib(args[1][2 + (i * 2) + 1]);
+ }
+
+ g_strfreev(args);
+}
+
+void qtest_memwrite_attrs(QTestState *s, uint64_t addr, const void *data,
+ size_t size, const char *attrs)
{
const uint8_t *ptr = data;
size_t i;
@@ -1458,14 +1559,26 @@ void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size)
sprintf(&enc[i * 2], "%02x", ptr[i]);
}
- qtest_sendf(s, "write 0x%" PRIx64 " 0x%zx 0x%s\n", addr, size, enc);
+ if (qtest_has_attrs(attrs)) {
+ qtest_sendf(s, "write 0x%" PRIx64 " 0x%zx 0x%s %s\n",
+ addr, size, enc, attrs);
+ } else {
+ qtest_sendf(s, "write 0x%" PRIx64 " 0x%zx 0x%s\n", addr, size, enc);
+ }
qtest_rsp(s);
g_free(enc);
}
-void qtest_memset(QTestState *s, uint64_t addr, uint8_t pattern, size_t size)
+void qtest_memset_attrs(QTestState *s, uint64_t addr, uint8_t pattern,
+ size_t size, const char *attrs)
{
- qtest_sendf(s, "memset 0x%" PRIx64 " 0x%zx 0x%02x\n", addr, size, pattern);
+ if (qtest_has_attrs(attrs)) {
+ qtest_sendf(s, "memset 0x%" PRIx64 " 0x%zx 0x%02x %s\n",
+ addr, size, pattern, attrs);
+ } else {
+ qtest_sendf(s, "memset 0x%" PRIx64 " 0x%zx 0x%02x\n",
+ addr, size, pattern);
+ }
qtest_rsp(s);
}
diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h
index 45217fb8dc0..7c0b239e5b5 100644
--- a/tests/qtest/libqtest.h
+++ b/tests/qtest/libqtest.h
@@ -679,6 +679,20 @@ uint64_t qtest_csr_call(QTestState *s, const char *name,
*/
void qtest_bufread(QTestState *s, uint64_t addr, void *data, size_t size);
+/**
+ * qtest_bufread_attrs:
+ * @s: #QTestState instance to operate on.
+ * @addr: Guest address to read from.
+ * @data: Pointer to where memory contents will be stored.
+ * @size: Number of bytes to read.
+ * @attrs: Optional transaction attributes string.
+ *
+ * Read guest memory into a buffer and receive using a base64 encoding with
+ * optional transaction attributes.
+ */
+void qtest_bufread_attrs(QTestState *s, uint64_t addr, void *data, size_t size,
+ const char *attrs);
+
/**
* qtest_memwrite:
* @s: #QTestState instance to operate on.
@@ -702,6 +716,21 @@ void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size)
void qtest_bufwrite(QTestState *s, uint64_t addr,
const void *data, size_t size);
+/**
+ * qtest_bufwrite_attrs:
+ * @s: #QTestState instance to operate on.
+ * @addr: Guest address to write to.
+ * @data: Pointer to the bytes that will be written to guest memory.
+ * @size: Number of bytes to write.
+ * @attrs: Optional transaction attributes string.
+ *
+ * Write a buffer to guest memory and transmit using a base64 encoding with
+ * optional transaction attributes.
+ */
+void qtest_bufwrite_attrs(QTestState *s, uint64_t addr,
+ const void *data, size_t size,
+ const char *attrs);
+
/**
* qtest_memset:
* @s: #QTestState instance to operate on.
@@ -713,6 +742,141 @@ void qtest_bufwrite(QTestState *s, uint64_t addr,
*/
void qtest_memset(QTestState *s, uint64_t addr, uint8_t patt, size_t size);
+/**
+ * qtest_writeb_attrs:
+ * @s: #QTestState instance to operate on.
+ * @addr: Guest address to write to.
+ * @value: Value being written.
+ * @attrs: Optional transaction attributes string.
+ *
+ * Writes an 8-bit value to guest memory with optional transaction attributes.
+ */
+void qtest_writeb_attrs(QTestState *s, uint64_t addr, uint8_t value,
+ const char *attrs);
+
+/**
+ * qtest_writew_attrs:
+ * @s: #QTestState instance to operate on.
+ * @addr: Guest address to write to.
+ * @value: Value being written.
+ * @attrs: Optional transaction attributes string.
+ *
+ * Writes a 16-bit value to guest memory with optional transaction attributes.
+ */
+void qtest_writew_attrs(QTestState *s, uint64_t addr, uint16_t value,
+ const char *attrs);
+
+/**
+ * qtest_writel_attrs:
+ * @s: #QTestState instance to operate on.
+ * @addr: Guest address to write to.
+ * @value: Value being written.
+ * @attrs: Optional transaction attributes string.
+ *
+ * Writes a 32-bit value to guest memory with optional transaction attributes.
+ */
+void qtest_writel_attrs(QTestState *s, uint64_t addr, uint32_t value,
+ const char *attrs);
+
+/**
+ * qtest_writeq_attrs:
+ * @s: #QTestState instance to operate on.
+ * @addr: Guest address to write to.
+ * @value: Value being written.
+ * @attrs: Optional transaction attributes string.
+ *
+ * Writes a 64-bit value to guest memory with optional transaction attributes.
+ */
+void qtest_writeq_attrs(QTestState *s, uint64_t addr, uint64_t value,
+ const char *attrs);
+
+/**
+ * qtest_readb_attrs:
+ * @s: #QTestState instance to operate on.
+ * @addr: Guest address to read from.
+ * @attrs: Optional transaction attributes string.
+ *
+ * Reads an 8-bit value from guest memory with optional transaction attributes.
+ *
+ * Returns: Value read.
+ */
+uint8_t qtest_readb_attrs(QTestState *s, uint64_t addr, const char *attrs);
+
+/**
+ * qtest_readw_attrs:
+ * @s: #QTestState instance to operate on.
+ * @addr: Guest address to read from.
+ * @attrs: Optional transaction attributes string.
+ *
+ * Reads a 16-bit value from guest memory with optional transaction attributes.
+ *
+ * Returns: Value read.
+ */
+uint16_t qtest_readw_attrs(QTestState *s, uint64_t addr, const char *attrs);
+
+/**
+ * qtest_readl_attrs:
+ * @s: #QTestState instance to operate on.
+ * @addr: Guest address to read from.
+ * @attrs: Optional transaction attributes string.
+ *
+ * Reads a 32-bit value from guest memory with optional transaction attributes.
+ *
+ * Returns: Value read.
+ */
+uint32_t qtest_readl_attrs(QTestState *s, uint64_t addr, const char *attrs);
+
+/**
+ * qtest_readq_attrs:
+ * @s: #QTestState instance to operate on.
+ * @addr: Guest address to read from.
+ * @attrs: Optional transaction attributes string.
+ *
+ * Reads a 64-bit value from guest memory with optional transaction attributes.
+ *
+ * Returns: Value read.
+ */
+uint64_t qtest_readq_attrs(QTestState *s, uint64_t addr, const char *attrs);
+
+/**
+ * qtest_memread_attrs:
+ * @s: #QTestState instance to operate on.
+ * @addr: Guest address to read from.
+ * @data: Pointer to where memory contents will be stored.
+ * @size: Number of bytes to read.
+ * @attrs: Optional transaction attributes string.
+ *
+ * Read guest memory into a buffer with optional transaction attributes.
+ */
+void qtest_memread_attrs(QTestState *s, uint64_t addr, void *data, size_t size,
+ const char *attrs);
+
+/**
+ * qtest_memwrite_attrs:
+ * @s: #QTestState instance to operate on.
+ * @addr: Guest address to write to.
+ * @data: Pointer to the bytes that will be written to guest memory.
+ * @size: Number of bytes to write.
+ * @attrs: Optional transaction attributes string.
+ *
+ * Write a buffer to guest memory with optional transaction attributes.
+ */
+void qtest_memwrite_attrs(QTestState *s, uint64_t addr, const void *data,
+ size_t size, const char *attrs);
+
+/**
+ * qtest_memset_attrs:
+ * @s: #QTestState instance to operate on.
+ * @addr: Guest address to write to.
+ * @patt: Byte pattern to fill the guest memory region with.
+ * @size: Number of bytes to write.
+ * @attrs: Optional transaction attributes string.
+ *
+ * Write a pattern to guest memory with optional transaction attributes.
+ */
+void qtest_memset_attrs(QTestState *s, uint64_t addr, uint8_t patt, size_t size,
+ const char *attrs);
+
/**
* qtest_clock_step_next:
* @s: #QTestState instance to operate on.
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [RFC v5 3/5] tests/qtest: Add raw command response helper
2026-08-14 3:56 [RFC v5 0/5] tests/qtest: Add memory-access attributes (secure/space) Tao Tang
2026-08-14 3:56 ` [RFC v5 1/5] tests/qtest: Add attrs support to qtest server memory commands Tao Tang
2026-08-14 3:56 ` [RFC v5 2/5] tests/qtest: Add libqtest attrs helpers for memory accesses Tao Tang
@ 2026-08-14 3:56 ` Tao Tang
2026-08-19 14:06 ` Fabiano Rosas
2026-08-14 3:56 ` [RFC v5 4/5] tests/qtest: Add qtest-attrs-test for memory access attrs Tao Tang
2026-08-14 3:56 ` [RFC v5 5/5] [NOT-MERGE] tests/qtest: add q35 SMM-only x86 attrs coverage Tao Tang
4 siblings, 1 reply; 9+ messages in thread
From: Tao Tang @ 2026-08-14 3:56 UTC (permalink / raw)
To: Fabiano Rosas, Laurent Vivier, Paolo Bonzini
Cc: qemu-devel, qemu-arm, Peter Maydell, Chen Baozi, Chao Liu,
Jim MacArthur, Tao Tang
Split raw qtest response tokenization and qtest_raw_cmd() from the
memory attributes helpers so this protocol support can be reviewed
independently.
Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
---
tests/qtest/libqtest.c | 27 +++++++++++++++++++++++++--
tests/qtest/libqtest.h | 13 +++++++++++++
2 files changed, 38 insertions(+), 2 deletions(-)
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
index 453d0dddd09..37aa69e1297 100644
--- a/tests/qtest/libqtest.c
+++ b/tests/qtest/libqtest.c
@@ -746,11 +746,10 @@ static GString *qtest_client_socket_recv_line(QTestState *s)
return line;
}
-static gchar **qtest_rsp_args(QTestState *s, int expected_args)
+static gchar **qtest_rsp_words(QTestState *s)
{
GString *line;
gchar **words;
- int i;
redo:
line = s->ops.recv_line(s);
@@ -780,6 +779,15 @@ redo:
}
g_assert(words[0] != NULL);
+
+ return words;
+}
+
+static gchar **qtest_rsp_args(QTestState *s, int expected_args)
+{
+ gchar **words = qtest_rsp_words(s);
+ int i;
+
g_assert_cmpstr(words[0], ==, "OK");
for (i = 0; i < expected_args; i++) {
@@ -789,6 +797,21 @@ redo:
return words;
}
+gchar **qtest_raw_cmd(QTestState *s, const char *fmt, ...)
+{
+ va_list ap;
+ gchar *str;
+
+ va_start(ap, fmt);
+ str = g_strdup_vprintf(fmt, ap);
+ va_end(ap);
+
+ s->ops.send(s, str);
+ g_free(str);
+
+ return qtest_rsp_words(s);
+}
+
static void qtest_rsp(QTestState *s)
{
gchar **words = qtest_rsp_args(s, 0);
diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h
index 7c0b239e5b5..715c363de30 100644
--- a/tests/qtest/libqtest.h
+++ b/tests/qtest/libqtest.h
@@ -239,6 +239,19 @@ void qtest_qmp_send(QTestState *s, const char *fmt, ...)
void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...)
G_GNUC_PRINTF(2, 3);
+/**
+ * qtest_raw_cmd:
+ * @s: #QTestState instance to operate on.
+ * @fmt: raw qtest protocol text to send, formatted like sprintf().
+ *
+ * Sends a raw qtest command and returns the response split on spaces.
+ * The response is not required to start with ``OK``; callers can inspect
+ * ``args[0]`` for ``OK``, ``ERR``, or ``FAIL`` and must free the returned
+ * vector with g_strfreev().
+ */
+gchar **qtest_raw_cmd(QTestState *s, const char *fmt, ...)
+ G_GNUC_PRINTF(2, 3);
+
/**
* qtest_socket_server:
* @socket_path: the UNIX domain socket path
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [RFC v5 4/5] tests/qtest: Add qtest-attrs-test for memory access attrs
2026-08-14 3:56 [RFC v5 0/5] tests/qtest: Add memory-access attributes (secure/space) Tao Tang
` (2 preceding siblings ...)
2026-08-14 3:56 ` [RFC v5 3/5] tests/qtest: Add raw command response helper Tao Tang
@ 2026-08-14 3:56 ` Tao Tang
2026-08-19 14:14 ` Fabiano Rosas
2026-08-14 3:56 ` [RFC v5 5/5] [NOT-MERGE] tests/qtest: add q35 SMM-only x86 attrs coverage Tao Tang
4 siblings, 1 reply; 9+ messages in thread
From: Tao Tang @ 2026-08-14 3:56 UTC (permalink / raw)
To: Fabiano Rosas, Laurent Vivier, Paolo Bonzini
Cc: qemu-devel, qemu-arm, Peter Maydell, Chen Baozi, Chao Liu,
Jim MacArthur, Tao Tang
Add qtest-attrs-test to exercise qtest memory access commands with attrs
on both aarch64 and x86.
The test covers:
- Arm virt,secure=on: scalar and bulk accesses across non-secure,
secure, and root spaces, plus negative coverage for realm and for
non-secure accesses into secure-only RAM
- x86 q35: normal accesses
- libqtest-single *_attrs shortcut wrappers
Add reusable response assertion helpers and structured attrs failure
helpers to libqtest. Use the structured APIs for negative memory access
tests, while retaining raw protocol commands for malformed command
shapes that the typed APIs cannot express.
On Arm, the test targets the virt machine's secure-only RAM window so
that the requested attrs must select the correct address space.
Also wire qtest-attrs-test into the aarch64 and i386/x86_64 qtest
builds.
Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
---
tests/qtest/libqtest.c | 156 +++++++++++++++++
tests/qtest/libqtest.h | 111 ++++++++++++
tests/qtest/meson.build | 7 +-
tests/qtest/qtest-attrs-test.c | 305 +++++++++++++++++++++++++++++++++
4 files changed, 577 insertions(+), 2 deletions(-)
create mode 100644 tests/qtest/qtest-attrs-test.c
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
index 37aa69e1297..533b6f34599 100644
--- a/tests/qtest/libqtest.c
+++ b/tests/qtest/libqtest.c
@@ -812,6 +812,37 @@ gchar **qtest_raw_cmd(QTestState *s, const char *fmt, ...)
return qtest_rsp_words(s);
}
+void qtest_cmd_assert_response(QTestState *s, const char *expected,
+ const char *fmt, ...)
+{
+ va_list ap;
+ g_autofree gchar *cmd = NULL;
+ g_autofree gchar *line = NULL;
+ g_auto(GStrv) response = NULL;
+
+ va_start(ap, fmt);
+ cmd = g_strdup_vprintf(fmt, ap);
+ va_end(ap);
+
+ response = qtest_raw_cmd(s, "%s", cmd);
+ line = g_strjoinv(" ", response);
+ g_assert_cmpstr(line, ==, expected);
+}
+
+void qtest_cmd_assert_failure(QTestState *s, const char *fmt, ...)
+{
+ va_list ap;
+ g_autofree gchar *cmd = NULL;
+ g_auto(GStrv) response = NULL;
+
+ va_start(ap, fmt);
+ cmd = g_strdup_vprintf(fmt, ap);
+ va_end(ap);
+
+ response = qtest_raw_cmd(s, "%s", cmd);
+ g_assert_cmpstr(response[0], ==, "ERR");
+}
+
static void qtest_rsp(QTestState *s)
{
gchar **words = qtest_rsp_args(s, 0);
@@ -819,6 +850,14 @@ static void qtest_rsp(QTestState *s)
g_strfreev(words);
}
+static void qtest_rsp_assert_failure(QTestState *s)
+{
+ gchar **words = qtest_rsp_words(s);
+
+ g_assert_cmpstr(words[0], ==, "ERR");
+ g_strfreev(words);
+}
+
static int qtest_query_target_endianness(QTestState *s)
{
gchar **args;
@@ -1438,6 +1477,24 @@ void qtest_bufwrite_attrs(QTestState *s, uint64_t addr, const void *data,
g_free(bdata);
}
+void qtest_bufwrite_attrs_assert_failure(QTestState *s, uint64_t addr,
+ const void *data, size_t size,
+ const char *attrs)
+{
+ gchar *bdata;
+
+ bdata = g_base64_encode(data, size);
+ qtest_sendf(s, "b64write 0x%" PRIx64 " 0x%zx ", addr, size);
+ s->ops.send(s, bdata);
+ if (qtest_has_attrs(attrs)) {
+ s->ops.send(s, " ");
+ s->ops.send(s, attrs);
+ }
+ s->ops.send(s, "\n");
+ qtest_rsp_assert_failure(s);
+ g_free(bdata);
+}
+
void qtest_bufread_attrs(QTestState *s, uint64_t addr, void *data, size_t size,
const char *attrs)
{
@@ -1462,6 +1519,18 @@ void qtest_bufread_attrs(QTestState *s, uint64_t addr, void *data, size_t size,
g_strfreev(args);
}
+void qtest_bufread_attrs_assert_failure(QTestState *s, uint64_t addr,
+ size_t size, const char *attrs)
+{
+ if (qtest_has_attrs(attrs)) {
+ qtest_sendf(s, "b64read 0x%" PRIx64 " 0x%zx %s\n",
+ addr, size, attrs);
+ } else {
+ qtest_sendf(s, "b64read 0x%" PRIx64 " 0x%zx\n", addr, size);
+ }
+ qtest_rsp_assert_failure(s);
+}
+
static void qtest_write_attrs(QTestState *s, const char *cmd,
uint64_t addr, uint64_t value,
const char *attrs)
@@ -1475,6 +1544,20 @@ static void qtest_write_attrs(QTestState *s, const char *cmd,
qtest_rsp(s);
}
+static void qtest_write_attrs_assert_failure(QTestState *s, const char *cmd,
+ uint64_t addr, uint64_t value,
+ const char *attrs)
+{
+ if (qtest_has_attrs(attrs)) {
+ qtest_sendf(s, "%s 0x%" PRIx64 " 0x%" PRIx64 " %s\n",
+ cmd, addr, value, attrs);
+ } else {
+ qtest_sendf(s, "%s 0x%" PRIx64 " 0x%" PRIx64 "\n",
+ cmd, addr, value);
+ }
+ qtest_rsp_assert_failure(s);
+}
+
static uint64_t qtest_read_attrs(QTestState *s, const char *cmd,
uint64_t addr, const char *attrs)
{
@@ -1495,12 +1578,29 @@ static uint64_t qtest_read_attrs(QTestState *s, const char *cmd,
return value;
}
+static void qtest_read_attrs_assert_failure(QTestState *s, const char *cmd,
+ uint64_t addr, const char *attrs)
+{
+ if (qtest_has_attrs(attrs)) {
+ qtest_sendf(s, "%s 0x%" PRIx64 " %s\n", cmd, addr, attrs);
+ } else {
+ qtest_sendf(s, "%s 0x%" PRIx64 "\n", cmd, addr);
+ }
+ qtest_rsp_assert_failure(s);
+}
+
void qtest_writeb_attrs(QTestState *s, uint64_t addr, uint8_t value,
const char *attrs)
{
qtest_write_attrs(s, "writeb", addr, value, attrs);
}
+void qtest_writeb_attrs_assert_failure(QTestState *s, uint64_t addr,
+ uint8_t value, const char *attrs)
+{
+ qtest_write_attrs_assert_failure(s, "writeb", addr, value, attrs);
+}
+
void qtest_writew_attrs(QTestState *s, uint64_t addr, uint16_t value,
const char *attrs)
{
@@ -1524,6 +1624,12 @@ uint8_t qtest_readb_attrs(QTestState *s, uint64_t addr, const char *attrs)
return qtest_read_attrs(s, "readb", addr, attrs);
}
+void qtest_readb_attrs_assert_failure(QTestState *s, uint64_t addr,
+ const char *attrs)
+{
+ qtest_read_attrs_assert_failure(s, "readb", addr, attrs);
+}
+
uint16_t qtest_readw_attrs(QTestState *s, uint64_t addr, const char *attrs)
{
return qtest_read_attrs(s, "readw", addr, attrs);
@@ -1565,6 +1671,17 @@ void qtest_memread_attrs(QTestState *s, uint64_t addr, void *data,
g_strfreev(args);
}
+void qtest_memread_attrs_assert_failure(QTestState *s, uint64_t addr,
+ size_t size, const char *attrs)
+{
+ if (qtest_has_attrs(attrs)) {
+ qtest_sendf(s, "read 0x%" PRIx64 " 0x%zx %s\n", addr, size, attrs);
+ } else {
+ qtest_sendf(s, "read 0x%" PRIx64 " 0x%zx\n", addr, size);
+ }
+ qtest_rsp_assert_failure(s);
+}
+
void qtest_memwrite_attrs(QTestState *s, uint64_t addr, const void *data,
size_t size, const char *attrs)
{
@@ -1592,6 +1709,31 @@ void qtest_memwrite_attrs(QTestState *s, uint64_t addr, const void *data,
g_free(enc);
}
+void qtest_memwrite_attrs_assert_failure(QTestState *s, uint64_t addr,
+ const void *data, size_t size,
+ const char *attrs)
+{
+ const uint8_t *ptr = data;
+ size_t i;
+ char *enc;
+
+ g_assert(size);
+ enc = g_malloc(2 * size + 1);
+
+ for (i = 0; i < size; i++) {
+ sprintf(&enc[i * 2], "%02x", ptr[i]);
+ }
+
+ if (qtest_has_attrs(attrs)) {
+ qtest_sendf(s, "write 0x%" PRIx64 " 0x%zx 0x%s %s\n",
+ addr, size, enc, attrs);
+ } else {
+ qtest_sendf(s, "write 0x%" PRIx64 " 0x%zx 0x%s\n", addr, size, enc);
+ }
+ qtest_rsp_assert_failure(s);
+ g_free(enc);
+}
+
void qtest_memset_attrs(QTestState *s, uint64_t addr, uint8_t pattern,
size_t size, const char *attrs)
{
@@ -1605,6 +1747,20 @@ void qtest_memset_attrs(QTestState *s, uint64_t addr, uint8_t pattern,
qtest_rsp(s);
}
+void qtest_memset_attrs_assert_failure(QTestState *s, uint64_t addr,
+ uint8_t pattern, size_t size,
+ const char *attrs)
+{
+ if (qtest_has_attrs(attrs)) {
+ qtest_sendf(s, "memset 0x%" PRIx64 " 0x%zx 0x%02x %s\n",
+ addr, size, pattern, attrs);
+ } else {
+ qtest_sendf(s, "memset 0x%" PRIx64 " 0x%zx 0x%02x\n",
+ addr, size, pattern);
+ }
+ qtest_rsp_assert_failure(s);
+}
+
QDict *qtest_vqmp_assert_failure_ref(QTestState *qts,
const char *fmt, va_list args)
{
diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h
index 715c363de30..db6adf565c5 100644
--- a/tests/qtest/libqtest.h
+++ b/tests/qtest/libqtest.h
@@ -252,6 +252,28 @@ void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...)
gchar **qtest_raw_cmd(QTestState *s, const char *fmt, ...)
G_GNUC_PRINTF(2, 3);
+/**
+ * qtest_cmd_assert_response:
+ * @s: #QTestState instance to operate on.
+ * @expected: expected qtest response line.
+ * @fmt: qtest protocol text to send, formatted like sprintf().
+ *
+ * Sends a qtest command and asserts that its response matches @expected.
+ */
+void qtest_cmd_assert_response(QTestState *s, const char *expected,
+ const char *fmt, ...)
+ G_GNUC_PRINTF(3, 4);
+
+/**
+ * qtest_cmd_assert_failure:
+ * @s: #QTestState instance to operate on.
+ * @fmt: qtest protocol text to send, formatted like sprintf().
+ *
+ * Sends a qtest command and asserts that its response starts with ``ERR``.
+ */
+void qtest_cmd_assert_failure(QTestState *s, const char *fmt, ...)
+ G_GNUC_PRINTF(2, 3);
+
/**
* qtest_socket_server:
* @socket_path: the UNIX domain socket path
@@ -706,6 +728,18 @@ void qtest_bufread(QTestState *s, uint64_t addr, void *data, size_t size);
void qtest_bufread_attrs(QTestState *s, uint64_t addr, void *data, size_t size,
const char *attrs);
+/**
+ * qtest_bufread_attrs_assert_failure:
+ * @s: #QTestState instance to operate on.
+ * @addr: Guest address to read from.
+ * @size: Number of bytes to read.
+ * @attrs: Transaction attributes string.
+ *
+ * Assert that an attributed base64 memory read fails.
+ */
+void qtest_bufread_attrs_assert_failure(QTestState *s, uint64_t addr,
+ size_t size, const char *attrs);
+
/**
* qtest_memwrite:
* @s: #QTestState instance to operate on.
@@ -744,6 +778,20 @@ void qtest_bufwrite_attrs(QTestState *s, uint64_t addr,
const void *data, size_t size,
const char *attrs);
+/**
+ * qtest_bufwrite_attrs_assert_failure:
+ * @s: #QTestState instance to operate on.
+ * @addr: Guest address to write to.
+ * @data: Pointer to the bytes to write.
+ * @size: Number of bytes to write.
+ * @attrs: Transaction attributes string.
+ *
+ * Assert that an attributed base64 memory write fails.
+ */
+void qtest_bufwrite_attrs_assert_failure(QTestState *s, uint64_t addr,
+ const void *data, size_t size,
+ const char *attrs);
+
/**
* qtest_memset:
* @s: #QTestState instance to operate on.
@@ -767,6 +815,18 @@ void qtest_memset(QTestState *s, uint64_t addr, uint8_t patt, size_t size);
void qtest_writeb_attrs(QTestState *s, uint64_t addr, uint8_t value,
const char *attrs);
+/**
+ * qtest_writeb_attrs_assert_failure:
+ * @s: #QTestState instance to operate on.
+ * @addr: Guest address to write to.
+ * @value: Value being written.
+ * @attrs: Transaction attributes string.
+ *
+ * Assert that an attributed 8-bit memory write fails.
+ */
+void qtest_writeb_attrs_assert_failure(QTestState *s, uint64_t addr,
+ uint8_t value, const char *attrs);
+
/**
* qtest_writew_attrs:
* @s: #QTestState instance to operate on.
@@ -815,6 +875,17 @@ void qtest_writeq_attrs(QTestState *s, uint64_t addr, uint64_t value,
*/
uint8_t qtest_readb_attrs(QTestState *s, uint64_t addr, const char *attrs);
+/**
+ * qtest_readb_attrs_assert_failure:
+ * @s: #QTestState instance to operate on.
+ * @addr: Guest address to read from.
+ * @attrs: Transaction attributes string.
+ *
+ * Assert that an attributed 8-bit memory read fails.
+ */
+void qtest_readb_attrs_assert_failure(QTestState *s, uint64_t addr,
+ const char *attrs);
+
/**
* qtest_readw_attrs:
* @s: #QTestState instance to operate on.
@@ -864,6 +935,18 @@ uint64_t qtest_readq_attrs(QTestState *s, uint64_t addr, const char *attrs);
void qtest_memread_attrs(QTestState *s, uint64_t addr, void *data, size_t size,
const char *attrs);
+/**
+ * qtest_memread_attrs_assert_failure:
+ * @s: #QTestState instance to operate on.
+ * @addr: Guest address to read from.
+ * @size: Number of bytes to read.
+ * @attrs: Transaction attributes string.
+ *
+ * Assert that an attributed hexadecimal memory read fails.
+ */
+void qtest_memread_attrs_assert_failure(QTestState *s, uint64_t addr,
+ size_t size, const char *attrs);
+
/**
* qtest_memwrite_attrs:
* @s: #QTestState instance to operate on.
@@ -877,6 +960,20 @@ void qtest_memread_attrs(QTestState *s, uint64_t addr, void *data, size_t size,
void qtest_memwrite_attrs(QTestState *s, uint64_t addr, const void *data,
size_t size, const char *attrs);
+/**
+ * qtest_memwrite_attrs_assert_failure:
+ * @s: #QTestState instance to operate on.
+ * @addr: Guest address to write to.
+ * @data: Pointer to the bytes to write.
+ * @size: Number of bytes to write.
+ * @attrs: Transaction attributes string.
+ *
+ * Assert that an attributed hexadecimal memory write fails.
+ */
+void qtest_memwrite_attrs_assert_failure(QTestState *s, uint64_t addr,
+ const void *data, size_t size,
+ const char *attrs);
+
/**
* qtest_memset_attrs:
* @s: #QTestState instance to operate on.
@@ -890,6 +987,20 @@ void qtest_memwrite_attrs(QTestState *s, uint64_t addr, const void *data,
void qtest_memset_attrs(QTestState *s, uint64_t addr, uint8_t patt, size_t size,
const char *attrs);
+/**
+ * qtest_memset_attrs_assert_failure:
+ * @s: #QTestState instance to operate on.
+ * @addr: Guest address to write to.
+ * @patt: Byte pattern to fill the guest memory region with.
+ * @size: Number of bytes to write.
+ * @attrs: Transaction attributes string.
+ *
+ * Assert that an attributed memory fill fails.
+ */
+void qtest_memset_attrs_assert_failure(QTestState *s, uint64_t addr,
+ uint8_t patt, size_t size,
+ const char *attrs);
+
/**
* qtest_clock_step_next:
* @s: #QTestState instance to operate on.
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index f7c7d06620e..fd59050fd27 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -120,6 +120,7 @@ qtests_i386 = \
'drive_del-test',
'cpu-plug-test',
'migration-test',
+ 'qtest-attrs-test',
]
if dbus_display and config_all_devices.has_key('CONFIG_VGA')
@@ -257,7 +258,8 @@ qtests_arm = \
(config_all_devices.has_key('CONFIG_STM32L4X5_SOC') and
config_all_devices.has_key('CONFIG_DM163')? ['dm163-test'] : []) + \
['arm-cpu-features',
- 'boot-serial-test']
+ 'boot-serial-test',
+ 'qtest-attrs-test',]
# TODO: once aarch64 TCG is fixed on ARM 32 bit host, make bios-tables-test unconditional
qtests_aarch64 = \
@@ -278,7 +280,8 @@ qtests_aarch64 = \
['arm-cpu-features',
'numa-test',
'boot-serial-test',
- 'migration-test']
+ 'migration-test',
+ 'qtest-attrs-test']
qtests_s390x = \
qtests_filter + \
diff --git a/tests/qtest/qtest-attrs-test.c b/tests/qtest/qtest-attrs-test.c
new file mode 100644
index 00000000000..f429000b29d
--- /dev/null
+++ b/tests/qtest/qtest-attrs-test.c
@@ -0,0 +1,305 @@
+/*
+ * QTest for memory access with transaction attributes
+ *
+ * Verify optional attrs argument support for qtest memory commands.
+ *
+ * Copyright (c) 2026 Phytium Technology
+ *
+ * Author:
+ * Tao Tang <tangtao1634@phytium.com.cn>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "libqtest-single.h"
+
+/*
+ * The Arm virt test uses both the default non-secure RAM at 0x4000_0000 and
+ * the secure-only RAM window at 0x0e00_0000. The x86 q35 test only exercises
+ * regular RAM that is visible from both the default and SMM address spaces.
+ */
+#define TEST_ADDR_OFFSET_NS 0x1000ULL
+#define TEST_ADDR_OFFSET_S 0xe000000ULL
+#define TEST_ARM_SEC_BASE 0x0ULL
+#define TEST_ARM_NS_BASE 0x40000000ULL
+#define TEST_X86_BASE 0x0ULL
+
+#define TEST_ADDR_ARM_S (TEST_ARM_SEC_BASE + TEST_ADDR_OFFSET_S)
+#define TEST_ADDR_ARM_NS (TEST_ARM_NS_BASE + TEST_ADDR_OFFSET_NS)
+#define TEST_ADDR_X86 (TEST_X86_BASE + TEST_ADDR_OFFSET_NS)
+
+#define ARM_MACHINE_ARGS "-machine virt,secure=on -accel tcg"
+#define X86_MACHINE_ARGS "-machine q35,smm=on -m 1G -accel tcg"
+
+static void test_arm_scalar_attrs(void)
+{
+ QTestState *qts;
+ uint8_t val;
+
+ if (!qtest_has_machine("virt")) {
+ g_test_skip("virt machine not available");
+ return;
+ }
+
+ qts = qtest_init(ARM_MACHINE_ARGS);
+
+ qtest_writeb_attrs(qts, TEST_ADDR_ARM_NS, 0x11, NULL);
+ val = qtest_readb_attrs(qts, TEST_ADDR_ARM_NS, NULL);
+ g_assert_cmpuint(val, ==, 0x11);
+
+ qtest_writeb_attrs(qts, TEST_ADDR_ARM_NS + 0x1, 0x22, "space=non-secure");
+ val = qtest_readb_attrs(qts, TEST_ADDR_ARM_NS + 0x1, "space=non-secure");
+ g_assert_cmpuint(val, ==, 0x22);
+
+ qtest_writeb_attrs(qts, TEST_ADDR_ARM_S + 0x2, 0x33, "secure");
+ val = qtest_readb_attrs(qts, TEST_ADDR_ARM_S + 0x2, "secure");
+ g_assert_cmpuint(val, ==, 0x33);
+
+ qtest_readb_attrs_assert_failure(qts, TEST_ADDR_ARM_NS + 0x2, "invalid");
+ qtest_readb_attrs_assert_failure(qts, TEST_ADDR_ARM_NS + 0x2,
+ "space=invalid");
+ qtest_cmd_assert_response(qts, "ERR too many arguments",
+ "writeb 0x%" PRIx64 " 0x44 secure extra\n",
+ (uint64_t)(TEST_ADDR_ARM_NS + 0x2));
+
+ qtest_writeb_attrs_assert_failure(qts, TEST_ADDR_ARM_S + 0x3, 0x44,
+ "space=realm");
+ qtest_readb_attrs_assert_failure(qts, TEST_ADDR_ARM_S + 0x3,
+ "space=realm");
+
+ qtest_writeb_attrs(qts, TEST_ADDR_ARM_S + 0x4, 0x55, "space=root");
+ val = qtest_readb_attrs(qts, TEST_ADDR_ARM_S + 0x4, "space=root");
+ g_assert_cmpuint(val, ==, 0x55);
+
+ qtest_writeb_attrs(qts, TEST_ADDR_ARM_S + 0x5, 0x66, "space=secure");
+ val = qtest_readb_attrs(qts, TEST_ADDR_ARM_S + 0x5, "space=secure");
+ g_assert_cmpuint(val, ==, 0x66);
+
+ qtest_writeb(qts, TEST_ADDR_ARM_NS + 0x6, 0x77);
+ val = qtest_readb(qts, TEST_ADDR_ARM_NS + 0x6);
+ g_assert_cmpuint(val, ==, 0x77);
+ val = qtest_readb_attrs(qts, TEST_ADDR_ARM_NS + 0x6, "space=non-secure");
+ g_assert_cmpuint(val, ==, 0x77);
+
+ qtest_writeb_attrs_assert_failure(qts, TEST_ADDR_ARM_S + 0x7, 0x77,
+ "space=non-secure");
+ qtest_readb_attrs_assert_failure(qts, TEST_ADDR_ARM_S + 0x7,
+ "space=non-secure");
+
+ qtest_quit(qts);
+}
+
+static void test_arm_bulk_attrs(void)
+{
+ QTestState *qts;
+ uint8_t wbuf[16] = {
+ 0x00, 0x11, 0x22, 0x33,
+ 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xaa, 0xbb,
+ 0xcc, 0xdd, 0xee, 0xff,
+ };
+ uint8_t b64_fail_buf[4] = { 1, 2, 3, 4 };
+ uint8_t rbuf[16];
+ size_t i;
+
+ if (!qtest_has_machine("virt")) {
+ g_test_skip("virt machine not available");
+ return;
+ }
+
+ qts = qtest_init(ARM_MACHINE_ARGS);
+
+ qtest_memwrite_attrs(qts, TEST_ADDR_ARM_NS + 0x100,
+ wbuf, sizeof(wbuf), NULL);
+ qtest_memread_attrs(qts, TEST_ADDR_ARM_NS + 0x100,
+ rbuf, sizeof(rbuf), NULL);
+ g_assert(memcmp(wbuf, rbuf, sizeof(wbuf)) == 0);
+
+ qtest_memwrite_attrs(qts, TEST_ADDR_ARM_NS + 0x200,
+ wbuf, sizeof(wbuf), "space=non-secure");
+ qtest_memread_attrs(qts, TEST_ADDR_ARM_NS + 0x200,
+ rbuf, sizeof(rbuf), "space=non-secure");
+ g_assert(memcmp(wbuf, rbuf, sizeof(wbuf)) == 0);
+
+ qtest_memwrite_attrs(qts, TEST_ADDR_ARM_S + 0x300,
+ wbuf, sizeof(wbuf), "secure");
+ qtest_memread_attrs(qts, TEST_ADDR_ARM_S + 0x300,
+ rbuf, sizeof(rbuf), "secure");
+ g_assert(memcmp(wbuf, rbuf, sizeof(wbuf)) == 0);
+
+ qtest_memset_attrs(qts, TEST_ADDR_ARM_S + 0x400,
+ 0xa5, sizeof(rbuf), "space=root");
+ qtest_memread_attrs(qts, TEST_ADDR_ARM_S + 0x400,
+ rbuf, sizeof(rbuf), "space=root");
+ for (i = 0; i < sizeof(rbuf); i++) {
+ g_assert_cmpuint(rbuf[i], ==, 0xa5);
+ }
+
+ qtest_bufwrite_attrs(qts, TEST_ADDR_ARM_NS + 0x500,
+ wbuf, sizeof(wbuf), "space=non-secure");
+ qtest_bufread_attrs(qts, TEST_ADDR_ARM_NS + 0x500,
+ rbuf, sizeof(rbuf), "space=non-secure");
+ g_assert(memcmp(wbuf, rbuf, sizeof(wbuf)) == 0);
+
+ qtest_bufwrite_attrs(qts, TEST_ADDR_ARM_S + 0x600,
+ wbuf, sizeof(wbuf), "secure");
+ qtest_bufread_attrs(qts, TEST_ADDR_ARM_S + 0x600,
+ rbuf, sizeof(rbuf), "secure");
+ g_assert(memcmp(wbuf, rbuf, sizeof(wbuf)) == 0);
+
+ qtest_memwrite(qts, TEST_ADDR_ARM_NS + 0x700, wbuf, 4);
+ qtest_memread(qts, TEST_ADDR_ARM_NS + 0x700, rbuf, 4);
+ g_assert(memcmp(wbuf, rbuf, 4) == 0);
+
+ qtest_memset(qts, TEST_ADDR_ARM_NS + 0x710, 0xa5, 4);
+ qtest_memread(qts, TEST_ADDR_ARM_NS + 0x710, rbuf, 4);
+ for (i = 0; i < 4; i++) {
+ g_assert_cmpuint(rbuf[i], ==, 0xa5);
+ }
+
+ qtest_bufwrite(qts, TEST_ADDR_ARM_NS + 0x720, wbuf, 4);
+ qtest_bufread(qts, TEST_ADDR_ARM_NS + 0x720, rbuf, 4);
+ g_assert(memcmp(wbuf, rbuf, 4) == 0);
+
+ qtest_memwrite_attrs_assert_failure(qts, TEST_ADDR_ARM_S + 0x730,
+ wbuf, 4, "space=non-secure");
+ qtest_memread_attrs_assert_failure(qts, TEST_ADDR_ARM_S + 0x730, 4,
+ "space=non-secure");
+ qtest_memset_attrs_assert_failure(qts, TEST_ADDR_ARM_S + 0x740,
+ 0xa5, 4, "space=non-secure");
+ qtest_bufwrite_attrs_assert_failure(qts, TEST_ADDR_ARM_S + 0x750,
+ b64_fail_buf, sizeof(b64_fail_buf),
+ "space=non-secure");
+ qtest_bufread_attrs_assert_failure(qts, TEST_ADDR_ARM_S + 0x750,
+ sizeof(b64_fail_buf),
+ "space=non-secure");
+ qtest_cmd_assert_response(qts, "ERR too many arguments",
+ "write 0x%" PRIx64 " 0x%zx 0x00112233 secure "
+ "extra\n",
+ (uint64_t)(TEST_ADDR_ARM_NS + 0x760),
+ (size_t)4);
+
+ qtest_quit(qts);
+}
+
+static void test_arm_single_shortcuts_attrs(void)
+{
+ uint8_t val;
+ uint8_t wbuf[4] = { 0x10, 0x20, 0x30, 0x40 };
+ uint8_t rbuf[4];
+
+ if (!qtest_has_machine("virt")) {
+ g_test_skip("virt machine not available");
+ return;
+ }
+
+ qtest_start(ARM_MACHINE_ARGS);
+
+ writeb_attrs(TEST_ADDR_ARM_S + 0x700, 0x5a, "secure");
+ val = readb_attrs(TEST_ADDR_ARM_S + 0x700, "secure");
+ g_assert_cmpuint(val, ==, 0x5a);
+
+ writel_attrs(TEST_ADDR_ARM_S + 0x704,
+ 0xa5a5a5a5, "space=root");
+ g_assert_cmphex(readl_attrs(TEST_ADDR_ARM_S + 0x704, "space=root"), ==,
+ 0xa5a5a5a5U);
+
+ memwrite_attrs(TEST_ADDR_ARM_NS + 0x708,
+ wbuf, sizeof(wbuf), "space=non-secure");
+ memread_attrs(TEST_ADDR_ARM_NS + 0x708,
+ rbuf, sizeof(rbuf), "space=non-secure");
+ g_assert(memcmp(wbuf, rbuf, sizeof(wbuf)) == 0);
+
+ qtest_end();
+}
+
+static void test_x86_scalar_attrs(void)
+{
+ QTestState *qts;
+ uint8_t val;
+
+ if (!qtest_has_machine("q35")) {
+ g_test_skip("q35 machine not available");
+ return;
+ }
+
+ qts = qtest_init(X86_MACHINE_ARGS);
+
+ qtest_writeb_attrs(qts, TEST_ADDR_X86, 0x11, NULL);
+ val = qtest_readb_attrs(qts, TEST_ADDR_X86, NULL);
+ g_assert_cmpuint(val, ==, 0x11);
+ val = qtest_readb_attrs(qts, TEST_ADDR_X86, "secure");
+ g_assert_cmpuint(val, ==, 0x11);
+
+ qtest_writeb_attrs(qts, TEST_ADDR_X86 + 0x1, 0x22, "secure");
+ val = qtest_readb_attrs(qts, TEST_ADDR_X86 + 0x1, "secure");
+ g_assert_cmpuint(val, ==, 0x22);
+ val = qtest_readb_attrs(qts, TEST_ADDR_X86 + 0x1, NULL);
+ g_assert_cmpuint(val, ==, 0x22);
+ qtest_readb_attrs_assert_failure(qts, TEST_ADDR_X86 + 0x2,
+ "space=secure");
+
+ qtest_quit(qts);
+}
+
+static void test_x86_bulk_attrs(void)
+{
+ QTestState *qts;
+ uint8_t wbuf[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ uint8_t rbuf[8];
+ size_t i;
+
+ if (!qtest_has_machine("q35")) {
+ g_test_skip("q35 machine not available");
+ return;
+ }
+
+ qts = qtest_init(X86_MACHINE_ARGS);
+
+ qtest_memwrite_attrs(qts, TEST_ADDR_X86 + 0x100, wbuf, sizeof(wbuf), NULL);
+ qtest_memread_attrs(qts, TEST_ADDR_X86 + 0x100,
+ rbuf, sizeof(rbuf), "secure");
+ g_assert(memcmp(wbuf, rbuf, sizeof(wbuf)) == 0);
+
+ qtest_memwrite_attrs(qts, TEST_ADDR_X86 + 0x180,
+ wbuf, sizeof(wbuf), "secure");
+ qtest_memread_attrs(qts, TEST_ADDR_X86 + 0x180,
+ rbuf, sizeof(rbuf), NULL);
+ g_assert(memcmp(wbuf, rbuf, sizeof(wbuf)) == 0);
+
+ qtest_memset_attrs(qts, TEST_ADDR_X86 + 0x200,
+ 0x3c, sizeof(rbuf), "secure");
+ qtest_memread_attrs(qts, TEST_ADDR_X86 + 0x200,
+ rbuf, sizeof(rbuf), NULL);
+ for (i = 0; i < sizeof(rbuf); i++) {
+ g_assert_cmpuint(rbuf[i], ==, 0x3c);
+ }
+
+ qtest_bufwrite_attrs(qts, TEST_ADDR_X86 + 0x280,
+ wbuf, sizeof(wbuf), NULL);
+ qtest_bufread_attrs(qts, TEST_ADDR_X86 + 0x280,
+ rbuf, sizeof(rbuf), "secure");
+ g_assert(memcmp(wbuf, rbuf, sizeof(wbuf)) == 0);
+ qtest_cmd_assert_response(qts, "ERR too many arguments",
+ "read 0x%" PRIx64 " 0x%zx secure extra\n",
+ (uint64_t)(TEST_ADDR_X86 + 0x300),
+ sizeof(rbuf));
+
+ qtest_quit(qts);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("/qtest/arm/attrs/scalar", test_arm_scalar_attrs);
+ qtest_add_func("/qtest/arm/attrs/bulk", test_arm_bulk_attrs);
+ qtest_add_func("/qtest/arm/attrs/single_shortcuts",
+ test_arm_single_shortcuts_attrs);
+ qtest_add_func("/qtest/x86/attrs/scalar", test_x86_scalar_attrs);
+ qtest_add_func("/qtest/x86/attrs/bulk", test_x86_bulk_attrs);
+
+ return g_test_run();
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [RFC v5 5/5] [NOT-MERGE] tests/qtest: add q35 SMM-only x86 attrs coverage
2026-08-14 3:56 [RFC v5 0/5] tests/qtest: Add memory-access attributes (secure/space) Tao Tang
` (3 preceding siblings ...)
2026-08-14 3:56 ` [RFC v5 4/5] tests/qtest: Add qtest-attrs-test for memory access attrs Tao Tang
@ 2026-08-14 3:56 ` Tao Tang
4 siblings, 0 replies; 9+ messages in thread
From: Tao Tang @ 2026-08-14 3:56 UTC (permalink / raw)
To: Fabiano Rosas, Laurent Vivier, Paolo Bonzini
Cc: qemu-devel, qemu-arm, Peter Maydell, Chen Baozi, Chao Liu,
Jim MacArthur, Tao Tang
Add a q35-only test path for x86 secure attrs by introducing an optional
test-only RAM region that is mapped only into the SMM address space.
The new qtest-x86-attrs-test enables this region with
`-global mch.x-smm-test-ram=on` and verifies that accesses with the
`secure` attribute reach the SMM-only region, while default accesses do
not. This provides the x86 cross-verification that qtest-attrs-test does
not cover, where normal RAM is visible from both the default and SMM
address spaces.
This is a NOT-MERGE commit.
Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
---
hw/pci-host/q35.c | 27 +++++
include/hw/pci-host/q35.h | 8 ++
tests/qtest/meson.build | 1 +
tests/qtest/qtest-x86-attrs-test.c | 170 +++++++++++++++++++++++++++++
4 files changed, 206 insertions(+)
create mode 100644 tests/qtest/qtest-x86-attrs-test.c
diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c
index f4556ad03a0..dfdfcde7cd1 100644
--- a/hw/pci-host/q35.c
+++ b/hw/pci-host/q35.c
@@ -635,12 +635,19 @@ static void mch_realize(PCIDevice *d, Error **errp)
int i;
MCHPCIState *mch = MCH_PCI_DEVICE(d);
+ ERRP_GUARD();
+
if (mch->ext_tseg_mbytes > MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_MAX) {
error_setg(errp, "invalid extended-tseg-mbytes value: %" PRIu16,
mch->ext_tseg_mbytes);
return;
}
+ if (mch->enable_smm_test_ram && !mch->has_smm_ranges) {
+ error_setg(errp, "x-smm-test-ram requires SMM support");
+ return;
+ }
+
/* setup pci memory mapping */
pc_pci_as_mapping_init(mch->system_memory, mch->pci_address_space);
@@ -671,6 +678,24 @@ static void mch_realize(PCIDevice *d, Error **errp)
if (mch->has_smm_ranges) {
mch_init_smram_regions(mch);
+
+ if (mch->enable_smm_test_ram) {
+ /*
+ * This is a QEMU-specific, test-only region. It is mapped only
+ * into mch->smram so qtest can verify that x86 secure attrs
+ * select the SMM address space rather than the default one.
+ */
+ memory_region_init_ram(&mch->smm_test_ram, OBJECT(mch),
+ "smm-test-ram",
+ MCH_HOST_BRIDGE_SMM_TEST_RAM_SIZE, errp);
+ if (*errp) {
+ return;
+ }
+ memory_region_add_subregion(&mch->smram,
+ MCH_HOST_BRIDGE_SMM_TEST_RAM_BASE,
+ &mch->smm_test_ram);
+ }
+
object_property_add_const_link(qdev_get_machine(), "smram",
OBJECT(&mch->smram));
}
@@ -680,6 +705,8 @@ static const Property mch_props[] = {
DEFINE_PROP_UINT16("extended-tseg-mbytes", MCHPCIState, ext_tseg_mbytes,
64),
DEFINE_PROP_BOOL("smbase-smram", MCHPCIState, has_smram_at_smbase, true),
+ DEFINE_PROP_BOOL("x-smm-test-ram", MCHPCIState, enable_smm_test_ram,
+ false),
};
static void mch_class_init(ObjectClass *klass, const void *data)
diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h
index f31a71010b6..5ba7541d321 100644
--- a/include/hw/pci-host/q35.h
+++ b/include/hw/pci-host/q35.h
@@ -49,8 +49,10 @@ struct MCHPCIState {
MemoryRegion smram, low_smram, high_smram;
MemoryRegion tseg_blackhole, tseg_window;
MemoryRegion smbase_blackhole, smbase_window;
+ MemoryRegion smm_test_ram;
bool has_smram_at_smbase;
bool has_smm_ranges;
+ bool enable_smm_test_ram;
Range pci_hole;
uint64_t below_4g_mem_size;
uint64_t above_4g_mem_size;
@@ -99,6 +101,12 @@ struct Q35PCIHost {
#define MCH_HOST_BRIDGE_PCIEXBAR_SIZE 8 /* 64bit register */
#define MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT 0xb0000000
#define MCH_HOST_BRIDGE_PCIEXBAR_MAX (0x10000000) /* 256M */
+/*
+ * Optional qtest-only RAM window used to expose an address that exists only
+ * in the SMM address space, so x86 secure attrs can be cross-checked.
+ */
+#define MCH_HOST_BRIDGE_SMM_TEST_RAM_BASE 0xfef00000
+#define MCH_HOST_BRIDGE_SMM_TEST_RAM_SIZE (64 * KiB)
#define MCH_HOST_BRIDGE_PCIEXBAR_ADMSK Q35_MASK(64, 35, 28)
#define MCH_HOST_BRIDGE_PCIEXBAR_128ADMSK ((uint64_t)(1 << 27))
#define MCH_HOST_BRIDGE_PCIEXBAR_64ADMSK ((uint64_t)(1 << 26))
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index fd59050fd27..b8eaa1180fa 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -121,6 +121,7 @@ qtests_i386 = \
'cpu-plug-test',
'migration-test',
'qtest-attrs-test',
+ 'qtest-x86-attrs-test',
]
if dbus_display and config_all_devices.has_key('CONFIG_VGA')
diff --git a/tests/qtest/qtest-x86-attrs-test.c b/tests/qtest/qtest-x86-attrs-test.c
new file mode 100644
index 00000000000..068ee8b7d37
--- /dev/null
+++ b/tests/qtest/qtest-x86-attrs-test.c
@@ -0,0 +1,170 @@
+/*
+ * QTest for x86 memory access with transaction attributes
+ *
+ * Verify q35 SMM address-space access with the secure attribute.
+ *
+ * Copyright (c) 2026 Phytium Technology
+ *
+ * Author:
+ * Tao Tang <tangtao1634@phytium.com.cn>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/cutils.h"
+#include "libqtest.h"
+
+#define TEST_ADDR_OFFSET_NS 0x1000ULL
+#define TEST_X86_BASE 0x0ULL
+#define TEST_X86_SMM_BASE 0xfef00000ULL
+
+#define TEST_ADDR_X86 (TEST_X86_BASE + TEST_ADDR_OFFSET_NS)
+
+#define X86_MACHINE_ARGS "-machine q35,smm=on -m 1G -accel tcg " \
+ "-global mch.x-smm-test-ram=on"
+
+static void assert_default_scalar_read_isolated(QTestState *qts, uint64_t addr,
+ char **before,
+ uint8_t secure_value)
+{
+ g_auto(GStrv) after = NULL;
+ uint64_t value;
+ int ret;
+
+ after = qtest_raw_cmd(qts, "readb 0x%" PRIx64 "\n", addr);
+
+ if (g_strcmp0(before[0], "ERR") == 0) {
+ g_assert_cmpstr(after[0], ==, "ERR");
+ return;
+ }
+
+ g_assert_cmpstr(before[0], ==, "OK");
+ g_assert_nonnull(before[1]);
+ g_assert_cmpstr(after[0], ==, "OK");
+ g_assert_nonnull(after[1]);
+ g_assert_cmpstr(after[1], ==, before[1]);
+
+ ret = qemu_strtou64(after[1], NULL, 0, &value);
+ g_assert_cmpint(ret, ==, 0);
+ g_assert_cmpuint(value, !=, secure_value);
+}
+
+static void assert_default_bulk_read_isolated(QTestState *qts, uint64_t addr,
+ char **before,
+ const uint8_t *expected,
+ size_t len)
+{
+ g_auto(GStrv) after = NULL;
+ g_autofree gchar *expected_b64 = NULL;
+
+ expected_b64 = g_base64_encode(expected, len);
+ after = qtest_raw_cmd(qts, "b64read 0x%" PRIx64 " 0x%zx\n", addr, len);
+
+ if (g_strcmp0(before[0], "ERR") == 0) {
+ g_assert_cmpstr(after[0], ==, "ERR");
+ return;
+ }
+
+ g_assert_cmpstr(before[0], ==, "OK");
+ g_assert_nonnull(before[1]);
+ g_assert_cmpstr(after[0], ==, "OK");
+ g_assert_nonnull(after[1]);
+ g_assert_cmpstr(after[1], ==, before[1]);
+ g_assert_cmpstr(after[1], !=, expected_b64);
+}
+
+static void test_x86_scalar_attrs(void)
+{
+ QTestState *qts;
+ g_auto(GStrv) before = NULL;
+ uint8_t val;
+
+ if (!qtest_has_machine("q35")) {
+ g_test_skip("q35 machine not available");
+ return;
+ }
+
+ qts = qtest_init(X86_MACHINE_ARGS);
+
+ qtest_writeb_attrs(qts, TEST_ADDR_X86, 0x11, NULL);
+ val = qtest_readb_attrs(qts, TEST_ADDR_X86, NULL);
+ g_assert_cmpuint(val, ==, 0x11);
+
+ qtest_writeb_attrs(qts, TEST_ADDR_X86 + 0x1, 0x22, "secure");
+ val = qtest_readb_attrs(qts, TEST_ADDR_X86 + 0x1, "secure");
+ g_assert_cmpuint(val, ==, 0x22);
+
+ before = qtest_raw_cmd(qts, "readb 0x%" PRIx64 "\n",
+ (uint64_t)(TEST_X86_SMM_BASE + 0x2));
+ qtest_writeb_attrs(qts, TEST_X86_SMM_BASE + 0x2, 0x33, "secure");
+ val = qtest_readb_attrs(qts, TEST_X86_SMM_BASE + 0x2, "secure");
+ g_assert_cmpuint(val, ==, 0x33);
+ assert_default_scalar_read_isolated(qts, TEST_X86_SMM_BASE + 0x2,
+ before, 0x33);
+
+ qtest_quit(qts);
+}
+
+static void test_x86_bulk_attrs(void)
+{
+ QTestState *qts;
+ g_auto(GStrv) before = NULL;
+ uint8_t wbuf[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ uint8_t rbuf[8];
+ size_t i;
+
+ if (!qtest_has_machine("q35")) {
+ g_test_skip("q35 machine not available");
+ return;
+ }
+
+ qts = qtest_init(X86_MACHINE_ARGS);
+
+ qtest_memwrite_attrs(qts, TEST_ADDR_X86 + 0x100, wbuf, sizeof(wbuf), NULL);
+ qtest_memread_attrs(qts, TEST_ADDR_X86 + 0x100, rbuf, sizeof(rbuf), NULL);
+ g_assert(memcmp(wbuf, rbuf, sizeof(wbuf)) == 0);
+
+ qtest_memwrite_attrs(qts, TEST_ADDR_X86 + 0x180,
+ wbuf, sizeof(wbuf), "secure");
+ qtest_memread_attrs(qts, TEST_ADDR_X86 + 0x180,
+ rbuf, sizeof(rbuf), "secure");
+ g_assert(memcmp(wbuf, rbuf, sizeof(wbuf)) == 0);
+
+ before = qtest_raw_cmd(qts, "b64read 0x%" PRIx64 " 0x%zx\n",
+ (uint64_t)(TEST_X86_SMM_BASE + 0x100),
+ sizeof(wbuf));
+ qtest_memwrite_attrs(qts, TEST_X86_SMM_BASE + 0x100,
+ wbuf, sizeof(wbuf), "secure");
+ qtest_memread_attrs(qts, TEST_X86_SMM_BASE + 0x100,
+ rbuf, sizeof(rbuf), "secure");
+ g_assert(memcmp(wbuf, rbuf, sizeof(wbuf)) == 0);
+ assert_default_bulk_read_isolated(qts, TEST_X86_SMM_BASE + 0x100, before,
+ wbuf, sizeof(wbuf));
+
+ qtest_memset_attrs(qts, TEST_X86_SMM_BASE + 0x120,
+ 0x5a, sizeof(rbuf), "secure");
+ qtest_memread_attrs(qts, TEST_X86_SMM_BASE + 0x120,
+ rbuf, sizeof(rbuf), "secure");
+ for (i = 0; i < sizeof(rbuf); i++) {
+ g_assert_cmpuint(rbuf[i], ==, 0x5a);
+ }
+
+ qtest_bufwrite_attrs(qts, TEST_X86_SMM_BASE + 0x200,
+ wbuf, sizeof(wbuf), "secure");
+ qtest_bufread_attrs(qts, TEST_X86_SMM_BASE + 0x200,
+ rbuf, sizeof(rbuf), "secure");
+ g_assert(memcmp(wbuf, rbuf, sizeof(wbuf)) == 0);
+
+ qtest_quit(qts);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("/qtest/x86/attrs/scalar", test_x86_scalar_attrs);
+ qtest_add_func("/qtest/x86/attrs/bulk", test_x86_bulk_attrs);
+
+ return g_test_run();
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [RFC v5 1/5] tests/qtest: Add attrs support to qtest server memory commands
2026-08-14 3:56 ` [RFC v5 1/5] tests/qtest: Add attrs support to qtest server memory commands Tao Tang
@ 2026-08-19 13:32 ` Fabiano Rosas
0 siblings, 0 replies; 9+ messages in thread
From: Fabiano Rosas @ 2026-08-19 13:32 UTC (permalink / raw)
To: Tao Tang, Laurent Vivier, Paolo Bonzini
Cc: qemu-devel, qemu-arm, Peter Maydell, Chen Baozi, Chao Liu,
Jim MacArthur, Tao Tang
Tao Tang <tangtao1634@phytium.com.cn> writes:
> Extend qtest memory access commands to accept an optional attrs argument.
>
> Supported attrs:
> - secure (x86/Arm)
> - space=non-secure|secure|root|realm (Arm-only)
>
> For memory commands, parse attrs, select the corresponding AddressSpace
> with cpu_asidx_from_attrs(), and issue accesses with the matching
> MemTxAttrs.
>
> Cover scalar and bulk memory commands, including:
> - read{b,w,l,q} / write{b,w,l,q}
> - read / write
> - b64read / b64write
> - memset
>
> Also reject invalid attrs and excessive arguments, and document the new
> command forms in qtest.c, and use qtest_send_memtx_error() to return
> ERR for memory access failures.
>
> Preserve legacy no-attrs semantics for compatibility: those commands
> continue to ignore MemTxResult and return OK.
>
> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
> ---
> system/qtest.c | 349 +++++++++++++++++++++++++++++++++++++++++--------
> 1 file changed, 293 insertions(+), 56 deletions(-)
>
> diff --git a/system/qtest.c b/system/qtest.c
> index cf301239177..ac863779bdf 100644
> --- a/system/qtest.c
> +++ b/system/qtest.c
> @@ -22,6 +22,7 @@
> #include "hw/core/qdev.h"
> #include "hw/core/irq.h"
> #include "hw/core/cpu.h"
> +#include "hw/arm/arm-security.h"
> #include "qemu/accel.h"
> #include "system/cpu-timers.h"
> #include "qemu/config-file.h"
> @@ -115,6 +116,14 @@ static void *qtest_server_send_opaque;
> * PIO and memory access:
> * """"""""""""""""""""""
> *
> + * Memory access commands allow specifying memory transaction attributes,
> + * which is useful for testing devices that behave differently based on
> + * security state (e.g., Arm TrustZone/CCA or System Management Mode in x86).
> + *
> + * Only the following attributes are supported by the qtest server:
> + * ``secure`` sets MemTxAttrs.secure=1 (x86/Arm).
> + * ``space=...`` is Arm-specific and accepts: non-secure, secure, root, realm.
> + *
> * .. code-block:: none
> *
> * > outb ADDR VALUE
> @@ -210,6 +219,36 @@ static void *qtest_server_send_opaque;
> * > memset ADDR SIZE VALUE
> * < OK
> *
> + * Memory commands accept an optional ATTRS argument after their existing
> + * operands:
> + *
> + * .. code-block:: none
> + *
> + * > read{b,w,l,q} ADDR [ATTRS]
> + * > write{b,w,l,q} ADDR VALUE [ATTRS]
> + * > read ADDR SIZE [ATTRS]
> + * > write ADDR SIZE DATA [ATTRS]
> + * > b64read ADDR SIZE [ATTRS]
> + * > b64write ADDR SIZE B64_DATA [ATTRS]
> + * > memset ADDR SIZE VALUE [ATTRS]
> + *
> + * Representative examples:
> + *
> + * .. code-block:: none
> + *
> + * > readl ADDR secure
> + * < OK VALUE
> + *
> + * .. code-block:: none
> + *
> + * > b64read ADDR SIZE space=root
> + * < OK B64_DATA
> + *
> + * .. code-block:: none
> + *
> + * > writeq ADDR VALUE space=realm
> + * < OK
> + *
> * ADDR, SIZE, VALUE are all integers parsed with strtoul() with a base of 0.
> * For 'memset' a zero size is permitted and does nothing.
> *
> @@ -352,6 +391,141 @@ static void qtest_install_gpio_out_intercept(DeviceState *dev, const char *name,
> *disconnected = qdev_intercept_gpio_out(dev, icpt, name, n);
> }
>
> +static bool qtest_parse_mem_attrs(CharFrontend *chr, const char *arg,
> + MemTxAttrs *attrs)
> +{
> + if (!arg) {
> + *attrs = MEMTXATTRS_UNSPECIFIED;
> + return true;
> + }
> +
> + if (strcmp(arg, "secure") == 0) {
> + *attrs = (MemTxAttrs){ .secure = 1 };
> + return true;
> + }
> +
> + if (strncmp(arg, "space=", 6) == 0) {
> + const char *space = arg + 6;
> + ARMSecuritySpace sec_space;
> +
> + if (!target_arm() && !target_aarch64()) {
> + qtest_send(chr, "ERR space=<...> is Arm-specific\n");
> + return false;
> + }
> +
> + if (strcmp(space, "non-secure") == 0) {
> + sec_space = ARMSS_NonSecure;
> + } else if (strcmp(space, "secure") == 0) {
> + sec_space = ARMSS_Secure;
> + } else if (strcmp(space, "root") == 0) {
> + sec_space = ARMSS_Root;
> + } else if (strcmp(space, "realm") == 0) {
> + sec_space = ARMSS_Realm;
> + } else {
> + qtest_send(chr, "ERR invalid space value. Valid space: "
> + "secure/non-secure/root/realm\n");
> + return false;
> + }
> +
> + *attrs = (MemTxAttrs){
> + .space = sec_space,
> + .secure = arm_space_is_secure(sec_space),
> + };
> + return true;
> + }
> +
> + qtest_send(chr, "ERR invalid attrs argument\n");
> + return false;
> +}
> +
> +static bool qtest_get_mem_as(CharFrontend *chr, MemTxAttrs attrs,
> + AddressSpace **as)
> +{
> + int asidx;
> +
> + /*
> + * cpu_asidx_from_attrs mainly uses attrs to call ->asidx_from_attrs. We use
> + * first_cpu as it's readily available.
> + */
> +
> + asidx = cpu_asidx_from_attrs(first_cpu, attrs);
> + *as = cpu_get_address_space(first_cpu, asidx);
> + if (!*as) {
> + qtest_send(chr, "ERR address space unavailable for attrs\n");
> + return false;
> + }
> +
> + return true;
> +}
> +
> +static void qtest_send_memtx_error(CharFrontend *chr, MemTxResult result)
> +{
> + qtest_sendf(chr, "ERR memory access failed 0x%x\n", result);
> +}
> +
> +static MemTxResult qtest_write_sized(AddressSpace *as, uint64_t addr,
> + MemTxAttrs attrs, uint64_t value,
> + char size)
> +{
> + switch (size) {
> + case 'b': {
> + uint8_t data = value;
> + return address_space_write(as, addr, attrs, &data, 1);
> + }
> + case 'w': {
> + uint16_t data = value;
> + tswap16s(&data);
> + return address_space_write(as, addr, attrs, &data, 2);
> + }
> + case 'l': {
> + uint32_t data = value;
> + tswap32s(&data);
> + return address_space_write(as, addr, attrs, &data, 4);
> + }
> + case 'q': {
> + uint64_t data = value;
> + tswap64s(&data);
> + return address_space_write(as, addr, attrs, &data, 8);
> + }
> + default:
> + g_assert_not_reached();
> + }
> +}
> +
> +static MemTxResult qtest_read_sized(AddressSpace *as, uint64_t addr,
> + MemTxAttrs attrs, char size,
> + uint64_t *value)
> +{
> + switch (size) {
> + case 'b': {
> + uint8_t data = 0;
> + MemTxResult result = address_space_read(as, addr, attrs, &data, 1);
> + *value = data;
> + return result;
> + }
> + case 'w': {
> + uint16_t data = 0;
> + MemTxResult result = address_space_read(as, addr, attrs, &data, 2);
> + *value = tswap16(data);
> + return result;
> + }
> + case 'l': {
> + uint32_t data = 0;
> + MemTxResult result = address_space_read(as, addr, attrs, &data, 4);
> + *value = tswap32(data);
> + return result;
> + }
> + case 'q': {
> + uint64_t data = 0;
> + MemTxResult result = address_space_read(as, addr, attrs, &data, 8);
> + *value = tswap64(data);
> + return result;
> + }
> + default:
> + g_assert_not_reached();
> + }
> +}
> +
> static void qtest_process_command(CharFrontend *chr, gchar **words)
> {
> const gchar *command;
> @@ -509,33 +683,29 @@ static void qtest_process_command(CharFrontend *chr, gchar **words)
> strcmp(words[0], "writeq") == 0) {
> uint64_t addr;
> uint64_t value;
> + MemTxResult result;
> + MemTxAttrs attrs;
> + AddressSpace *as;
> int ret;
>
> g_assert(words[1] && words[2]);
> + if (words[3] && words[4]) {
> + qtest_send(chr, "ERR too many arguments\n");
> + return;
> + }
> ret = qemu_strtou64(words[1], NULL, 0, &addr);
> g_assert(ret == 0);
> ret = qemu_strtou64(words[2], NULL, 0, &value);
> g_assert(ret == 0);
> + if (!qtest_parse_mem_attrs(chr, words[3], &attrs) ||
> + !qtest_get_mem_as(chr, attrs, &as)) {
> + return;
> + }
>
> - if (words[0][5] == 'b') {
> - uint8_t data = value;
> - address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
> - &data, 1);
> - } else if (words[0][5] == 'w') {
> - uint16_t data = value;
> - tswap16s(&data);
> - address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
> - &data, 2);
> - } else if (words[0][5] == 'l') {
> - uint32_t data = value;
> - tswap32s(&data);
> - address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
> - &data, 4);
> - } else if (words[0][5] == 'q') {
> - uint64_t data = value;
> - tswap64s(&data);
> - address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
> - &data, 8);
> + result = qtest_write_sized(as, addr, attrs, value, words[0][5]);
> + if (words[3] && result != MEMTX_OK) {
> + qtest_send_memtx_error(chr, result);
> + return;
> }
> qtest_send(chr, "OK\n");
> } else if (strcmp(words[0], "readb") == 0 ||
> @@ -543,51 +713,62 @@ static void qtest_process_command(CharFrontend *chr, gchar **words)
> strcmp(words[0], "readl") == 0 ||
> strcmp(words[0], "readq") == 0) {
> uint64_t addr;
> - uint64_t value = UINT64_C(-1);
> + uint64_t value = 0;
> + MemTxResult result;
> + MemTxAttrs attrs;
> + AddressSpace *as;
> int ret;
>
> g_assert(words[1]);
> + if (words[2] && words[3]) {
> + qtest_send(chr, "ERR too many arguments\n");
> + return;
> + }
> ret = qemu_strtou64(words[1], NULL, 0, &addr);
> g_assert(ret == 0);
> + if (!qtest_parse_mem_attrs(chr, words[2], &attrs) ||
> + !qtest_get_mem_as(chr, attrs, &as)) {
> + return;
> + }
>
> - if (words[0][4] == 'b') {
> - uint8_t data;
> - address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
> - &data, 1);
> - value = data;
> - } else if (words[0][4] == 'w') {
> - uint16_t data;
> - address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
> - &data, 2);
> - value = tswap16(data);
> - } else if (words[0][4] == 'l') {
> - uint32_t data;
> - address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
> - &data, 4);
> - value = tswap32(data);
> - } else if (words[0][4] == 'q') {
> - address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
> - &value, 8);
> - tswap64s(&value);
> + result = qtest_read_sized(as, addr, attrs, words[0][4], &value);
> + if (words[2] && result != MEMTX_OK) {
> + qtest_send_memtx_error(chr, result);
> + return;
> }
> qtest_sendf(chr, "OK 0x%016" PRIx64 "\n", value);
> } else if (strcmp(words[0], "read") == 0) {
> g_autoptr(GString) enc = NULL;
> uint64_t addr, len;
> uint8_t *data;
> + MemTxResult result;
> + MemTxAttrs attrs;
> + AddressSpace *as;
> int ret;
>
> g_assert(words[1] && words[2]);
> + if (words[3] && words[4]) {
> + qtest_send(chr, "ERR too many arguments\n");
> + return;
> + }
> ret = qemu_strtou64(words[1], NULL, 0, &addr);
> g_assert(ret == 0);
> ret = qemu_strtou64(words[2], NULL, 0, &len);
> g_assert(ret == 0);
> /* We'd send garbage to libqtest if len is 0 */
> g_assert(len);
> + if (!qtest_parse_mem_attrs(chr, words[3], &attrs) ||
> + !qtest_get_mem_as(chr, attrs, &as)) {
> + return;
> + }
>
> - data = g_malloc(len);
> - address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data,
> - len);
> + data = g_malloc0(len);
> + result = address_space_read(as, addr, attrs, data, len);
> + if (words[3] && result != MEMTX_OK) {
> + g_free(data);
> + qtest_send_memtx_error(chr, result);
> + return;
> + }
>
> enc = qemu_hexdump_line(NULL, data, len, 0, 0);
>
> @@ -598,17 +779,32 @@ static void qtest_process_command(CharFrontend *chr, gchar **words)
> uint64_t addr, len;
> uint8_t *data;
> gchar *b64_data;
> + MemTxResult result;
> + MemTxAttrs attrs;
> + AddressSpace *as;
> int ret;
>
> g_assert(words[1] && words[2]);
> + if (words[3] && words[4]) {
> + qtest_send(chr, "ERR too many arguments\n");
> + return;
> + }
> ret = qemu_strtou64(words[1], NULL, 0, &addr);
> g_assert(ret == 0);
> ret = qemu_strtou64(words[2], NULL, 0, &len);
> g_assert(ret == 0);
> + if (!qtest_parse_mem_attrs(chr, words[3], &attrs) ||
> + !qtest_get_mem_as(chr, attrs, &as)) {
> + return;
> + }
>
> - data = g_malloc(len);
> - address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data,
> - len);
> + data = g_malloc0(len);
> + result = address_space_read(as, addr, attrs, data, len);
> + if (words[3] && result != MEMTX_OK) {
> + g_free(data);
> + qtest_send_memtx_error(chr, result);
> + return;
> + }
> b64_data = g_base64_encode(data, len);
> qtest_sendf(chr, "OK %s\n", b64_data);
>
> @@ -618,14 +814,20 @@ static void qtest_process_command(CharFrontend *chr, gchar **words)
> uint64_t addr, len, i;
> uint8_t *data;
> size_t data_len;
> + MemTxResult result;
> + MemTxAttrs attrs;
> + AddressSpace *as;
> int ret;
>
> g_assert(words[1] && words[2] && words[3]);
> + if (words[4] && words[5]) {
> + qtest_send(chr, "ERR too many arguments\n");
> + return;
> + }
> ret = qemu_strtou64(words[1], NULL, 0, &addr);
> g_assert(ret == 0);
> ret = qemu_strtou64(words[2], NULL, 0, &len);
> g_assert(ret == 0);
> -
> data_len = strlen(words[3]);
> if (data_len < 3) {
> qtest_send(chr, "ERR invalid argument size\n");
> @@ -641,47 +843,75 @@ static void qtest_process_command(CharFrontend *chr, gchar **words)
> data[i] = 0;
> }
> }
> - address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data,
> - len);
> + if (!qtest_parse_mem_attrs(chr, words[4], &attrs) ||
> + !qtest_get_mem_as(chr, attrs, &as)) {
> + g_free(data);
> + return;
> + }
> + result = address_space_write(as, addr, attrs, data, len);
> g_free(data);
> + if (words[4] && result != MEMTX_OK) {
> + qtest_send_memtx_error(chr, result);
> + return;
> + }
>
> qtest_send(chr, "OK\n");
> } else if (strcmp(words[0], "memset") == 0) {
> uint64_t addr, len;
> - uint8_t *data;
> unsigned long pattern;
> + MemTxResult result;
> + MemTxAttrs attrs;
> + AddressSpace *as;
> int ret;
>
> g_assert(words[1] && words[2] && words[3]);
> + if (words[4] && words[5]) {
> + qtest_send(chr, "ERR too many arguments\n");
> + return;
> + }
> ret = qemu_strtou64(words[1], NULL, 0, &addr);
> g_assert(ret == 0);
> ret = qemu_strtou64(words[2], NULL, 0, &len);
> g_assert(ret == 0);
> ret = qemu_strtoul(words[3], NULL, 0, &pattern);
> g_assert(ret == 0);
> + if (!qtest_parse_mem_attrs(chr, words[4], &attrs) ||
> + !qtest_get_mem_as(chr, attrs, &as)) {
> + return;
> + }
>
> if (len) {
> - data = g_malloc(len);
> + uint8_t *data = g_malloc(len);
> +
> memset(data, pattern, len);
> - address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
> - data, len);
> + result = address_space_write(as, addr, attrs, data, len);
> g_free(data);
> + if (words[4] && result != MEMTX_OK) {
> + qtest_send_memtx_error(chr, result);
> + return;
> + }
> }
>
> qtest_send(chr, "OK\n");
> - } else if (strcmp(words[0], "b64write") == 0) {
> + } else if (strcmp(words[0], "b64write") == 0) {
> uint64_t addr, len;
> uint8_t *data;
> size_t data_len;
> gsize out_len;
> + MemTxResult result;
> + MemTxAttrs attrs;
> + AddressSpace *as;
> int ret;
>
> g_assert(words[1] && words[2] && words[3]);
> + if (words[4] && words[5]) {
> + qtest_send(chr, "ERR too many arguments\n");
> + return;
> + }
> ret = qemu_strtou64(words[1], NULL, 0, &addr);
> g_assert(ret == 0);
> ret = qemu_strtou64(words[2], NULL, 0, &len);
> g_assert(ret == 0);
> -
> data_len = strlen(words[3]);
> if (data_len < 3) {
> qtest_send(chr, "ERR invalid argument size\n");
> @@ -696,8 +926,15 @@ static void qtest_process_command(CharFrontend *chr, gchar **words)
> out_len = MIN(out_len, len);
> }
>
> - address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data,
> - out_len);
> + if (!qtest_parse_mem_attrs(chr, words[4], &attrs) ||
> + !qtest_get_mem_as(chr, attrs, &as)) {
> + return;
> + }
> + result = address_space_write(as, addr, attrs, data, out_len);
> + if (words[4] && result != MEMTX_OK) {
> + qtest_send_memtx_error(chr, result);
> + return;
> + }
>
> qtest_send(chr, "OK\n");
> } else if (strcmp(words[0], "endianness") == 0) {
Reviewed-by: Fabiano Rosas <farosas@suse.de>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [RFC v5 3/5] tests/qtest: Add raw command response helper
2026-08-14 3:56 ` [RFC v5 3/5] tests/qtest: Add raw command response helper Tao Tang
@ 2026-08-19 14:06 ` Fabiano Rosas
0 siblings, 0 replies; 9+ messages in thread
From: Fabiano Rosas @ 2026-08-19 14:06 UTC (permalink / raw)
To: Tao Tang, Laurent Vivier, Paolo Bonzini
Cc: qemu-devel, qemu-arm, Peter Maydell, Chen Baozi, Chao Liu,
Jim MacArthur, Tao Tang
Tao Tang <tangtao1634@phytium.com.cn> writes:
> Split raw qtest response tokenization and qtest_raw_cmd() from the
> memory attributes helpers so this protocol support can be reviewed
> independently.
>
> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
> ---
> tests/qtest/libqtest.c | 27 +++++++++++++++++++++++++--
> tests/qtest/libqtest.h | 13 +++++++++++++
> 2 files changed, 38 insertions(+), 2 deletions(-)
>
> diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
> index 453d0dddd09..37aa69e1297 100644
> --- a/tests/qtest/libqtest.c
> +++ b/tests/qtest/libqtest.c
> @@ -746,11 +746,10 @@ static GString *qtest_client_socket_recv_line(QTestState *s)
> return line;
> }
>
> -static gchar **qtest_rsp_args(QTestState *s, int expected_args)
> +static gchar **qtest_rsp_words(QTestState *s)
> {
> GString *line;
> gchar **words;
> - int i;
>
> redo:
> line = s->ops.recv_line(s);
> @@ -780,6 +779,15 @@ redo:
> }
>
> g_assert(words[0] != NULL);
> +
> + return words;
> +}
> +
> +static gchar **qtest_rsp_args(QTestState *s, int expected_args)
> +{
> + gchar **words = qtest_rsp_words(s);
> + int i;
> +
> g_assert_cmpstr(words[0], ==, "OK");
>
> for (i = 0; i < expected_args; i++) {
> @@ -789,6 +797,21 @@ redo:
> return words;
> }
>
> +gchar **qtest_raw_cmd(QTestState *s, const char *fmt, ...)
> +{
> + va_list ap;
> + gchar *str;
> +
> + va_start(ap, fmt);
> + str = g_strdup_vprintf(fmt, ap);
> + va_end(ap);
> +
> + s->ops.send(s, str);
> + g_free(str);
> +
> + return qtest_rsp_words(s);
> +}
> +
> static void qtest_rsp(QTestState *s)
> {
> gchar **words = qtest_rsp_args(s, 0);
> diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h
> index 7c0b239e5b5..715c363de30 100644
> --- a/tests/qtest/libqtest.h
> +++ b/tests/qtest/libqtest.h
> @@ -239,6 +239,19 @@ void qtest_qmp_send(QTestState *s, const char *fmt, ...)
> void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...)
> G_GNUC_PRINTF(2, 3);
>
> +/**
> + * qtest_raw_cmd:
> + * @s: #QTestState instance to operate on.
> + * @fmt: raw qtest protocol text to send, formatted like sprintf().
> + *
> + * Sends a raw qtest command and returns the response split on spaces.
> + * The response is not required to start with ``OK``; callers can inspect
> + * ``args[0]`` for ``OK``, ``ERR``, or ``FAIL`` and must free the returned
> + * vector with g_strfreev().
> + */
> +gchar **qtest_raw_cmd(QTestState *s, const char *fmt, ...)
> + G_GNUC_PRINTF(2, 3);
> +
> /**
> * qtest_socket_server:
> * @socket_path: the UNIX domain socket path
Reviewed-by: Fabiano Rosas <farosas@suse.de>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [RFC v5 4/5] tests/qtest: Add qtest-attrs-test for memory access attrs
2026-08-14 3:56 ` [RFC v5 4/5] tests/qtest: Add qtest-attrs-test for memory access attrs Tao Tang
@ 2026-08-19 14:14 ` Fabiano Rosas
0 siblings, 0 replies; 9+ messages in thread
From: Fabiano Rosas @ 2026-08-19 14:14 UTC (permalink / raw)
To: Tao Tang, Laurent Vivier, Paolo Bonzini
Cc: qemu-devel, qemu-arm, Peter Maydell, Chen Baozi, Chao Liu,
Jim MacArthur, Tao Tang
Tao Tang <tangtao1634@phytium.com.cn> writes:
> Add qtest-attrs-test to exercise qtest memory access commands with attrs
> on both aarch64 and x86.
>
> The test covers:
> - Arm virt,secure=on: scalar and bulk accesses across non-secure,
> secure, and root spaces, plus negative coverage for realm and for
> non-secure accesses into secure-only RAM
> - x86 q35: normal accesses
> - libqtest-single *_attrs shortcut wrappers
>
> Add reusable response assertion helpers and structured attrs failure
> helpers to libqtest.
Can't you re-order patch 3/5 before 2/5 and put these generic changes in
2/5 as well?
> Use the structured APIs for negative memory access
> tests, while retaining raw protocol commands for malformed command
> shapes that the typed APIs cannot express.
>
> On Arm, the test targets the virt machine's secure-only RAM window so
> that the requested attrs must select the correct address space.
>
> Also wire qtest-attrs-test into the aarch64 and i386/x86_64 qtest
> builds.
>
> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
> ---
> tests/qtest/libqtest.c | 156 +++++++++++++++++
> tests/qtest/libqtest.h | 111 ++++++++++++
> tests/qtest/meson.build | 7 +-
> tests/qtest/qtest-attrs-test.c | 305 +++++++++++++++++++++++++++++++++
> 4 files changed, 577 insertions(+), 2 deletions(-)
> create mode 100644 tests/qtest/qtest-attrs-test.c
>
> diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
> index 37aa69e1297..533b6f34599 100644
> --- a/tests/qtest/libqtest.c
> +++ b/tests/qtest/libqtest.c
The repetition introduced in this part is annoying, but let's not suffer
because of it, we can clean it up later. We could probably unify the
attrs and non-attrs versions using variadic functions as with the qmp
helpers.
For libqtest:
Reviewed-by: Fabiano Rosas <farosas@suse.de>
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-08-19 14:15 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-14 3:56 [RFC v5 0/5] tests/qtest: Add memory-access attributes (secure/space) Tao Tang
2026-08-14 3:56 ` [RFC v5 1/5] tests/qtest: Add attrs support to qtest server memory commands Tao Tang
2026-08-19 13:32 ` Fabiano Rosas
2026-08-14 3:56 ` [RFC v5 2/5] tests/qtest: Add libqtest attrs helpers for memory accesses Tao Tang
2026-08-14 3:56 ` [RFC v5 3/5] tests/qtest: Add raw command response helper Tao Tang
2026-08-19 14:06 ` Fabiano Rosas
2026-08-14 3:56 ` [RFC v5 4/5] tests/qtest: Add qtest-attrs-test for memory access attrs Tao Tang
2026-08-19 14:14 ` Fabiano Rosas
2026-08-14 3:56 ` [RFC v5 5/5] [NOT-MERGE] tests/qtest: add q35 SMM-only x86 attrs coverage Tao Tang
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.