Add qtest-attrs-test to exercise qtest memory commands with optional attrs
on aarch64 (virt, secure=on) and x86 (pc, tcg).
The test covers:
- ARM (virt machine): exercises all supported ARM security spaces
(non-secure, secure, root, realm).
- x86 (pc machine): exercises secure attrs accesses
(SMM address-space path).
The test also covers libqtest-single *_attrs shortcut wrappers and
verifies that space=non-secure behaves like omitting attrs.
Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
---
tests/qtest/meson.build | 4 +-
tests/qtest/qtest-attrs-test.c | 234 +++++++++++++++++++++++++++++++++
2 files changed, 237 insertions(+), 1 deletion(-)
create mode 100644 tests/qtest/qtest-attrs-test.c
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index be4fa627b5..c11759e23f 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -115,6 +115,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')
@@ -270,7 +271,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 0000000000..ce204c2c95
--- /dev/null
+++ b/tests/qtest/qtest-attrs-test.c
@@ -0,0 +1,234 @@
+/*
+ * 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"
+
+/*
+ * Default RAM size is 128 MiB on both machines used below.
+ * Keep test addresses in low RAM and away from device MMIO regions.
+ */
+#define TEST_ADDR_OFFSET 0x1000ULL
+#define TEST_ARM_BASE 0x40000000ULL
+#define TEST_X86_BASE 0x0ULL
+
+#define TEST_ADDR_ARM (TEST_ARM_BASE + TEST_ADDR_OFFSET)
+#define TEST_ADDR_X86 (TEST_X86_BASE + TEST_ADDR_OFFSET)
+
+#define ARM_MACHINE_ARGS "-machine virt,secure=on -cpu cortex-a57"
+#define X86_MACHINE_ARGS "-machine pc -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, 0x11, NULL);
+ val = qtest_readb_attrs(qts, TEST_ADDR_ARM, NULL);
+ g_assert_cmpuint(val, ==, 0x11);
+
+ qtest_writeb_attrs(qts, TEST_ADDR_ARM + 0x1, 0x22, "secure");
+ val = qtest_readb_attrs(qts, TEST_ADDR_ARM + 0x1, "secure");
+ g_assert_cmpuint(val, ==, 0x22);
+
+ qtest_writeb_attrs(qts, TEST_ADDR_ARM + 0x2, 0x33, "space=realm");
+ val = qtest_readb_attrs(qts, TEST_ADDR_ARM + 0x2, "space=realm");
+ g_assert_cmpuint(val, ==, 0x33);
+
+ qtest_writeb_attrs(qts, TEST_ADDR_ARM + 0x3, 0x44, "space=root");
+ val = qtest_readb_attrs(qts, TEST_ADDR_ARM + 0x3, "space=root");
+ g_assert_cmpuint(val, ==, 0x44);
+
+ qtest_writeb_attrs(qts, TEST_ADDR_ARM + 0x4, 0x55, "space=secure");
+ val = qtest_readb_attrs(qts, TEST_ADDR_ARM + 0x4, "space=secure");
+ g_assert_cmpuint(val, ==, 0x55);
+
+ /* space=non-secure is equivalent to no attrs argument */
+ qtest_writeb_attrs(qts, TEST_ADDR_ARM + 0x5, 0x66, "space=non-secure");
+ val = qtest_readb_attrs(qts, TEST_ADDR_ARM + 0x5, NULL);
+ g_assert_cmpuint(val, ==, 0x66);
+
+ qtest_writeb_attrs(qts, TEST_ADDR_ARM + 0x6, 0x77, NULL);
+ val = qtest_readb_attrs(qts, TEST_ADDR_ARM + 0x6, "space=non-secure");
+ g_assert_cmpuint(val, ==, 0x77);
+
+ 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 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 + 0x100,
+ wbuf, sizeof(wbuf), NULL);
+ qtest_memread_attrs(qts, TEST_ADDR_ARM + 0x100,
+ rbuf, sizeof(rbuf), NULL);
+ g_assert(memcmp(wbuf, rbuf, sizeof(wbuf)) == 0);
+
+ qtest_memwrite_attrs(qts, TEST_ADDR_ARM + 0x200,
+ wbuf, sizeof(wbuf), "secure");
+ qtest_memread_attrs(qts, TEST_ADDR_ARM + 0x200,
+ rbuf, sizeof(rbuf), "secure");
+ g_assert(memcmp(wbuf, rbuf, sizeof(wbuf)) == 0);
+
+ qtest_memwrite_attrs(qts, TEST_ADDR_ARM + 0x300,
+ wbuf, sizeof(wbuf), "space=realm");
+ qtest_memread_attrs(qts, TEST_ADDR_ARM + 0x300,
+ rbuf, sizeof(rbuf), "space=realm");
+ g_assert(memcmp(wbuf, rbuf, sizeof(wbuf)) == 0);
+
+ qtest_memset_attrs(qts, TEST_ADDR_ARM + 0x400,
+ 0xa5, sizeof(rbuf), "space=root");
+ qtest_memread_attrs(qts, TEST_ADDR_ARM + 0x400,
+ rbuf, sizeof(rbuf), "space=root");
+ for (i = 0; i < sizeof(rbuf); i++) {
+ g_assert_cmpuint(rbuf[i], ==, 0xa5);
+ }
+
+ qtest_memset_attrs(qts, TEST_ADDR_ARM + 0x500,
+ 0x5a, sizeof(rbuf), "space=non-secure");
+ qtest_memread_attrs(qts, TEST_ADDR_ARM + 0x500,
+ rbuf, sizeof(rbuf), NULL);
+ for (i = 0; i < sizeof(rbuf); i++) {
+ g_assert_cmpuint(rbuf[i], ==, 0x5a);
+ }
+
+ 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 + 0x600, 0x5a, "secure");
+ val = readb_attrs(TEST_ADDR_ARM + 0x600, "secure");
+ g_assert_cmpuint(val, ==, 0x5a);
+
+ writel_attrs(TEST_ADDR_ARM + 0x604,
+ 0xa5a5a5a5, "space=realm");
+ g_assert_cmphex(readl_attrs(TEST_ADDR_ARM + 0x604, "space=realm"), ==,
+ 0xa5a5a5a5U);
+
+ memwrite_attrs(TEST_ADDR_ARM + 0x608,
+ wbuf, sizeof(wbuf), "space=non-secure");
+ memread_attrs(TEST_ADDR_ARM + 0x608,
+ rbuf, sizeof(rbuf), NULL);
+ 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("pc")) {
+ g_test_skip("pc 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, 0xaa, "secure");
+ val = qtest_readb_attrs(qts, TEST_ADDR_X86 + 0x1, "secure");
+ g_assert_cmpuint(val, ==, 0xaa);
+
+ 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("pc")) {
+ g_test_skip("pc 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);
+
+ qtest_memset_attrs(qts, TEST_ADDR_X86 + 0x200,
+ 0x3c, sizeof(rbuf), "secure");
+ qtest_memread_attrs(qts, TEST_ADDR_X86 + 0x200,
+ rbuf, sizeof(rbuf), "secure");
+ for (i = 0; i < sizeof(rbuf); i++) {
+ g_assert_cmpuint(rbuf[i], ==, 0x3c);
+ }
+
+ 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
On Wed, 11 Mar 2026 at 15:50, Tao Tang <tangtao1634@phytium.com.cn> wrote:
>
> Add qtest-attrs-test to exercise qtest memory commands with optional attrs
> on aarch64 (virt, secure=on) and x86 (pc, tcg).
>
> The test covers:
> - ARM (virt machine): exercises all supported ARM security spaces
> (non-secure, secure, root, realm).
> - x86 (pc machine): exercises secure attrs accesses
> (SMM address-space path).
>
> The test also covers libqtest-single *_attrs shortcut wrappers and
> verifies that space=non-secure behaves like omitting attrs.
>
> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
> ---
> tests/qtest/meson.build | 4 +-
> tests/qtest/qtest-attrs-test.c | 234 +++++++++++++++++++++++++++++++++
> 2 files changed, 237 insertions(+), 1 deletion(-)
> create mode 100644 tests/qtest/qtest-attrs-test.c
>
> diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
> index be4fa627b5..c11759e23f 100644
> --- a/tests/qtest/meson.build
> +++ b/tests/qtest/meson.build
> @@ -115,6 +115,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')
> @@ -270,7 +271,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 0000000000..ce204c2c95
> --- /dev/null
> +++ b/tests/qtest/qtest-attrs-test.c
> @@ -0,0 +1,234 @@
> +/*
> + * 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"
> +
> +/*
> + * Default RAM size is 128 MiB on both machines used below.
> + * Keep test addresses in low RAM and away from device MMIO regions.
> + */
> +#define TEST_ADDR_OFFSET 0x1000ULL
> +#define TEST_ARM_BASE 0x40000000ULL
> +#define TEST_X86_BASE 0x0ULL
> +
> +#define TEST_ADDR_ARM (TEST_ARM_BASE + TEST_ADDR_OFFSET)
> +#define TEST_ADDR_X86 (TEST_X86_BASE + TEST_ADDR_OFFSET)
> +
> +#define ARM_MACHINE_ARGS "-machine virt,secure=on -cpu cortex-a57"
> +#define X86_MACHINE_ARGS "-machine pc -accel tcg"
You start a virt machine with secure=on, but you are only
testing the RAM, which is mapped regardless of the security
space, so this will not notice any bugs where we don't actually
access with the correct attributes.
It would be more interesting to look at the secure-only
RAM (which is at 0x0e00_0000), which should only be accessible
via the secure or root spaces. (I'm not sure what qtest will
do for attempted access via non-secure and realm, where the
address will have nothing mapped there. You'll have to check.)
I don't know if the x86 PC machine has a similar bit of RAM
or whatever that behaves differently for secure and non-secure
accesses.
thanks
-- PMM
Hi Peter,
On 3/13/2026 3:23 AM, Peter Maydell wrote:
> On Wed, 11 Mar 2026 at 15:50, Tao Tang <tangtao1634@phytium.com.cn> wrote:
>> Add qtest-attrs-test to exercise qtest memory commands with optional attrs
>> on aarch64 (virt, secure=on) and x86 (pc, tcg).
>>
>> The test covers:
>> - ARM (virt machine): exercises all supported ARM security spaces
>> (non-secure, secure, root, realm).
>> - x86 (pc machine): exercises secure attrs accesses
>> (SMM address-space path).
>>
>> The test also covers libqtest-single *_attrs shortcut wrappers and
>> verifies that space=non-secure behaves like omitting attrs.
>>
>> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
>> ---
>> tests/qtest/meson.build | 4 +-
>> tests/qtest/qtest-attrs-test.c | 234 +++++++++++++++++++++++++++++++++
>> 2 files changed, 237 insertions(+), 1 deletion(-)
>> create mode 100644 tests/qtest/qtest-attrs-test.c
>>
>> diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
>> index be4fa627b5..c11759e23f 100644
>> --- a/tests/qtest/meson.build
>> +++ b/tests/qtest/meson.build
>> @@ -115,6 +115,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')
>> @@ -270,7 +271,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 0000000000..ce204c2c95
>> --- /dev/null
>> +++ b/tests/qtest/qtest-attrs-test.c
>> @@ -0,0 +1,234 @@
>> +/*
>> + * 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"
>> +
>> +/*
>> + * Default RAM size is 128 MiB on both machines used below.
>> + * Keep test addresses in low RAM and away from device MMIO regions.
>> + */
>> +#define TEST_ADDR_OFFSET 0x1000ULL
>> +#define TEST_ARM_BASE 0x40000000ULL
>> +#define TEST_X86_BASE 0x0ULL
>> +
>> +#define TEST_ADDR_ARM (TEST_ARM_BASE + TEST_ADDR_OFFSET)
>> +#define TEST_ADDR_X86 (TEST_X86_BASE + TEST_ADDR_OFFSET)
>> +
>> +#define ARM_MACHINE_ARGS "-machine virt,secure=on -cpu cortex-a57"
>> +#define X86_MACHINE_ARGS "-machine pc -accel tcg"
> You start a virt machine with secure=on, but you are only
> testing the RAM, which is mapped regardless of the security
> space, so this will not notice any bugs where we don't actually
> access with the correct attributes.
>
> It would be more interesting to look at the secure-only
> RAM (which is at 0x0e00_0000), which should only be accessible
> via the secure or root spaces. (I'm not sure what qtest will
> do for attempted access via non-secure and realm, where the
> address will have nothing mapped there. You'll have to check.)
I just tested the secure-only RAM path as you suggested. For accesses
from the non-secure side, I now see:
"Invalid read/write at addr 0xE000000, size 1, region '(null)',
reason: rejected"
which is consistent with the secure-only RAM not being mapped in the
non-secure address space. So I agree that this is a much better target
for validating whether the requested attrs are actually being used.
While testing this, I also found another bug in the qtest implementation:
qtest_read/write_sized() currently ignore the MemTxResult returned by
address_space_read/write, so access failures are effectively handled
silently. That is not correct. For failed reads/writes, qtest should
return a non-OK response so the failure can be asserted properly in
qtest_rsp_args() instead of being treated as success.
I'll fix this in v3 as well.
>
> I don't know if the x86 PC machine has a similar bit of RAM
> or whatever that behaves differently for secure and non-secure
> accesses.
Unlike the Arm virt case, where secure=on gives a very clear secure-only
RAM target, the x86 side seems less straightforward to me too.
From the current x86 code, it looks like the normal and SMM address
spaces are both created ( tcg_cpu_realizefn in
target/i386/tcg/system/tcg-cpu.c ), with the SMM view built by
overlaying /machine/smram on top of the normal memory
view(tcg_cpu_machine_done in target/i386/tcg/system/tcg-cpu.c).
I also looked into the x86 code to see whether there is an obvious
SMM-specific MemoryRegion that would make a good test target, and I
found some SMM-related logic in hw/pci-host/q35.c ( such as function
mch_* ), but that path is complex enough that I am not confident I fully
understand the intended testing model.
Would it make sense to ask x86 experts what the best way is to test this
on the PC machine?
>
> thanks
> -- PMM
Thanks for the review,
Tao
© 2016 - 2026 Red Hat, Inc.