tangtao1634 <[email protected]> writes:
From: Tao Tang <[email protected]>
Add a tiny, test-only DMA source dedicated to exercising the SMMUv3 model.
The device purposefully avoids a realistic PCIe/platform implementation and
instead routes DMA requests straight into the SMMU, so that qtests can
populate STE/CD/PTE with known values and observe translation and data
movement deterministically, without booting any firmware or guest kernel.
Motivation
----------
Bringing up and regression-testing the SMMU in emulation often depends on a
large and flaky software stack (enumeration, drivers, PCIe fabric). For the
class of tests that only need to (1) program translation structures and (2)
trigger DMA at a precise time, that stack adds noise, slows CI, and makes
failures harder to attribute to the SMMU itself. A hermetic DMA source
keeps the surface area small and the results reproducible.
What this device is (and is not)
--------------------------------
* It is a minimal DMA producer solely for SMMU tests.
* It is NOT a faithful PCIe Endpoint nor a platform device.
* It is NOT added to any machine by default and remains test-only.
Signed-off-by: Tao Tang <[email protected]>
---
hw/misc/Kconfig | 5 +
hw/misc/meson.build | 1 +
hw/misc/smmu-testdev.c | 943 +++++++++++++++++++++++++++++++++
include/hw/misc/smmu-testdev.h | 402 ++++++++++++++
4 files changed, 1351 insertions(+)
create mode 100644 hw/misc/smmu-testdev.c
create mode 100644 include/hw/misc/smmu-testdev.h
------------------------------<snip>------------------------------
------------------------------<snip>------------------------------
+/* ---- Debug helpers for printing current translation configuration ---- */
+static void G_GNUC_PRINTF(2, 3)
+smmu_testdev_debug(const SMMUTestDevState *s, const char *fmt, ...)
+{
+ va_list ap;
+ g_autofree char *msg = NULL;
+
+ if (!s->debug_log) {
+ return;
+ }
+
+ va_start(ap, fmt);
+ msg = g_strdup_vprintf(fmt, ap);
+ va_end(ap);
+
+ if (qemu_log_enabled()) {
+ qemu_log("%s", msg);
+ } else {
+ fprintf(stderr, "%s", msg);
+ }
+}
Why are we re-inventing logging here? Either use qemu_log or probably
better define tracepoints.
+
+static MemTxAttrs mk_attrs_from_space(SMMUTestDevSpace space)
+{
+ MemTxAttrs a = {0};
+ if (!smmu_testdev_space_supported(space)) {
+ g_assert_not_reached();
Isn't this the same as just saying:
g_assert(smmu_testdev_space_supported(space));
?
+static void smmu_testdev_build_translation(SMMUTestDevState *s)
+{
+ smmu_testdev_debug(s, "smmu_testdev_build_translation: stage=%s
s1_space=%s"
+ " s2_space=%s\n", std_mode_to_str(s->trans_mode),
+ std_space_to_str(s->s1_space),
+ std_space_to_str(s->s2_space));
tracepoint
+ if (st != 0) {
+ printf("Writing STE error! status: %x\n", st);
qemu_log_mask(LOG_GUEST_ERROR, ...
+ AddressSpace *as = space_to_as(bank_sp);
+ if (!as) {
+ printf("push_cfgi_cmd: space %d not supported\n", bank_sp);
qemu_log_mask(LOG_UNIMP?
+ return;
+ }
+ int ret = address_space_write(as, entry_pa, a,
+ words, sizeof(words));
+ smmu_testdev_debug(s, "push_cfgi_cmd ret %d\n", ret);
tracepoint
+
+ /* update PROD to trigger command handler */
+ uint32_t new_prod = (prod + 1) & ((1u << (log2size + 1)) - 1u);
+ address_space_stl_le(&address_space_memory,
+ s->smmu_base + bank_off + 0x98,
+ new_prod, MEMTXATTRS_UNSPECIFIED, &res);
+ smmu_testdev_debug(s, "last res %d\n", res);
tracepoint but really see if it can be merged with above.
+ smmu_testdev_debug(s, "smmu_testdev_maybe_run_dma: dma_pending: %d\n",
+ s->dma_pending);
tracepoint
+
+ s->dma_pending = false;
+
+ if (!s->dma_len || s->dma_len > DMA_MAX_LEN) {
+ s->dma_result = DMA_ERR_BAD_LEN;
+ return;
+ }
+
+ g_autofree uint8_t *buf = g_malloc(s->dma_len);
+ MemTxResult res;
Keep local vars at the top of the block.
+static const Property smmu_testdev_properties[] = {
+ DEFINE_PROP_UINT32("device", SMMUTestDevState, cfg_dev, 0),
+ DEFINE_PROP_UINT32("function", SMMUTestDevState, cfg_fn, 1),
+ DEFINE_PROP_BOOL("debug-log", SMMUTestDevState, debug_log,
false),
We have tracepoints so we don't need this flag.
+/* BAR0 registers (offsets) */
+enum {
+ STD_REG_ID = 0x00,
+ STD_REG_ATTR_NS = 0x04,
+ STD_REG_SMMU_BASE_LO = 0x20,
+ STD_REG_SMMU_BASE_HI = 0x24,
+ STD_REG_DMA_IOVA_LO = 0x28,
+ STD_REG_DMA_IOVA_HI = 0x2C,
+ STD_REG_DMA_LEN = 0x30,
+ STD_REG_DMA_DIR = 0x34,
+ STD_REG_DMA_RESULT = 0x38,
+ STD_REG_DMA_DBELL = 0x3C,
+ /* Extended controls for DMA attributes/mode */
+ STD_REG_DMA_MODE = 0x40,
+ STD_REG_DMA_ATTRS = 0x44,
+ /* Translation controls */
+ STD_REG_TRANS_MODE = 0x48,
+ STD_REG_S1_SPACE = 0x4C,
+ STD_REG_S2_SPACE = 0x50,
+ STD_REG_TRANS_DBELL = 0x54,
+ STD_REG_TRANS_STATUS = 0x58,
+ /* Clear helper-built tables/descriptors (write-any to trigger) */
+ STD_REG_TRANS_CLEAR = 0x5C,
+};
Aren't we just duplicating the anonymous enum in smmu-testdev.c here? I
think qtest is allowed to include qemu headers so lets just have a
common set please.