From nobody Mon Feb 9 09:00:18 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1769602567; cv=none; d=zohomail.com; s=zohoarc; b=Qw8aC6o9TSucx1/bxn11ZICNWYqBfCaPSiFNZmk0DBKW0IqLi0dm5mOE8bA/0UP01WbBirmaeHlUyN8Qt1RSmSbgcwZEdPhqFAHAw1qMnYp1oI/98ZKxOgIxfmvJoESQb+ASt4RxhQ+CsmYKFHTcyWZd0p8b3Kopl+moh63Px6s= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1769602567; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=kvO7CVRTZw8Sxag3xE+c7nd2KfrVcatVDAOhlnuMyOo=; b=V/A8bfdFM25KosZruuShSAVs9fM/dU5h5Eby26PaRzFMBDIN2f9+VHMYHvtpth1ar2scG8BFBzLbpj20WnSEHikBtK7UqsltLlACOqcZ7Lil4/c8jCzwEUZn9OHBgqg2q2IeRo2ZByjzEtvbdOyDw1oXpEdyaKUW/uLdQ6syEvs= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 176960256740745.66898034047472; Wed, 28 Jan 2026 04:16:07 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vl4Oo-0004Lo-6b; Wed, 28 Jan 2026 07:11:54 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vl4NA-00044p-IO for qemu-devel@nongnu.org; Wed, 28 Jan 2026 07:10:12 -0500 Received: from mail-pf1-x444.google.com ([2607:f8b0:4864:20::444]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1vl4N6-0008Gc-C1 for qemu-devel@nongnu.org; Wed, 28 Jan 2026 07:10:11 -0500 Received: by mail-pf1-x444.google.com with SMTP id d2e1a72fcca58-81f39438187so3618719b3a.2 for ; Wed, 28 Jan 2026 04:10:05 -0800 (PST) Received: from ZEVORN-PC.bbrouter ([183.195.22.224]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-82379c24083sm2503153b3a.55.2026.01.28.04.10.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 28 Jan 2026 04:10:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1769602204; x=1770207004; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=kvO7CVRTZw8Sxag3xE+c7nd2KfrVcatVDAOhlnuMyOo=; b=Evwt+NRP2TWyy8UTIT4NWz6MOBXD2rbfZiSJD2TxoiFehTud0x6Pgve9NEQV8UWhGx hFYr6qU6Yk318K2XuSMGX1zuz5tH80BDYXyPlaKVOqmIuPpo++k70tln7zWJGdfbpUXB TW81TB6rPTthlzwNC7uNMhv7zzVAQ25hdjiNL5xruj4Y+U/2yHkcfJkktgGJmBub4nVr JIC6TeAfCTFXyil0v4G5yv3C27f5aLqqxcSp2Bf87p3YGlShuqyPoRyBcg26yO2n1B34 Zk56nqo5ZoAJsvCsgJfXU2tNVJjE9TbkE9ex9Hrl5rY8Tmheee9HiW5okTaT4Iajicbr K0uA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769602204; x=1770207004; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=kvO7CVRTZw8Sxag3xE+c7nd2KfrVcatVDAOhlnuMyOo=; b=W/fxYD+nQsNU99NQVjwD+xhSTiwAF74tzQcCF5UMd79Q0uj11l9suddUsg/hVMANhw TiQewW7PcHpARJFYCseg8yDWYDsOUX3Jxu3E+TCW35WdSIz1wk6pY44bJchrkbZsw23g Z1VwWDNZZsdwnUq4rWP6Gk+KLBCFXcMzhcAZYFfMKOKxip36tNZljFvIE0tn7zbw4Gqb jOSZBnIOO6v1Ch6E6cqobYENcsUGFJf9UGoGZKIY/rf9DDuHOEOkkQTl++QNptzjyANc HQQ0ro4Q0XxGKOU4bLckJuE3FWjW12R820qjwc+4a7+y0U+DKfLA9L9gH2jKDap5V+Qz 8Z3w== X-Gm-Message-State: AOJu0Yy4C/8gM2IxedRjIx15i70AiZ1bIVUBJXt9mrv/tnkYPT/XPao6 +mlMbuL2U6UaLu1WAxfjrPS/oXmcyt4TlKLW/tFwjpIs4twLgtQ5I4/q X-Gm-Gg: AZuq6aJJ/E09DHUXAcznFFZG0CW0yzOUjdtnG9KMmLgwyoo4RYLWSysAwCcQsMrLPbx tdVxwZ7vZBDJ4HtbogfwLJ/MrubOq4b4MyD1hjRiuu/Yj1LdAcUZfn7G3nzK5lfIzwdsfkYQAN8 t1xHFpmXY+w2ydlEe/plo/WnfgcovLDvr9P8Fv8xSlSFnYDrZAAjJbxe5xb0dt9HDB+vXcJt9+P GLC1yFUPS2va9cgCe8cBj14pkR2aF97fmgQ3d1+21BdYXqUXwbN/eg/6hhEF4tn4R0L44QcBvX5 jB0zaE0IhX4VUS2HI2RCyQraZDq1jqUG7JMpB/KYTrLc3hdbHPLkbamSv+Y/VXDHM/PbEAjrasm 7NU2ani3FYOFw366MAA/ldLG55XBZlUhkScjLqChSzbNx+Q8Gy8vDuR6M80WaDd4MhU8HoTXxc4 QyAwJyB+ad7Vs6pZ3eARnZzzlAQlQzF5MqEqe6OtVHZ0OgWIImqIz+cuFnxTk= X-Received: by 2002:a05:6a00:1398:b0:81f:3afe:2824 with SMTP id d2e1a72fcca58-823691845e8mr5006867b3a.24.1769602204034; Wed, 28 Jan 2026 04:10:04 -0800 (PST) From: Chao Liu To: Alistair Francis , Daniel Henrique Barboza , Palmer Dabbelt , Weiwei Li , Liu Zhiwei , Fabiano Rosas , Laurent Vivier , Paolo Bonzini , Tao Tang Cc: qemu-devel@nongnu.org, qemu-riscv@nongnu.org, hust-os-kernel-patches@googlegroups.com, Chao Liu Subject: [RFC PATCH v1 2/2] tests/qtest: Add RISC-V IOMMU bare-metal test Date: Wed, 28 Jan 2026 20:09:09 +0800 Message-ID: X-Mailer: git-send-email 2.52.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::444; envelope-from=chao.liu.zevorn@gmail.com; helo=mail-pf1-x444.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1769602571759158500 Content-Type: text/plain; charset="utf-8" Add a qtest suite for the RISC-V IOMMU PCI device on the virt machine. The test exercises bare, S-stage, G-stage, and nested translation paths using iommu-testdev and the qos-riscv-iommu helpers. The test validates: - Device context (DC) configuration - SV39 page table walks for S-stage translation - SV39x4 page table walks for G-stage translation - Nested translation combining both stages - FCTL register constraints This provides regression coverage for the RISC-V IOMMU implementation without requiring a full guest OS boot. Signed-off-by: Chao Liu Reviewed-by: Daniel Henrique Barboza --- MAINTAINERS | 1 + tests/qtest/iommu-riscv-test.c | 279 +++++++++++++++++++++++++++++++++ tests/qtest/meson.build | 5 +- 3 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 tests/qtest/iommu-riscv-test.c diff --git a/MAINTAINERS b/MAINTAINERS index 894e05bd2c..c7400c83d2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -347,6 +347,7 @@ F: common-user/host/riscv* F: tests/functional/riscv32 F: tests/functional/riscv64 F: tests/tcg/riscv64/ +F: tests/qtest/iommu-riscv-test.c =20 RISC-V XThead* extensions M: Christoph Muellner diff --git a/tests/qtest/iommu-riscv-test.c b/tests/qtest/iommu-riscv-test.c new file mode 100644 index 0000000000..9438578e1e --- /dev/null +++ b/tests/qtest/iommu-riscv-test.c @@ -0,0 +1,279 @@ +/* + * QTest testcase for RISC-V IOMMU with iommu-testdev + * + * This QTest file is used to test the RISC-V IOMMU with iommu-testdev so = that + * we can test RISC-V IOMMU without any guest kernel or firmware. + * + * Copyright (c) 2026 Chao Liu + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/pci.h" +#include "libqos/generic-pcihost.h" +#include "hw/pci/pci_regs.h" +#include "hw/misc/iommu-testdev.h" +#include "hw/riscv/riscv-iommu-bits.h" +#include "libqos/qos-riscv-iommu.h" +#include "libqos/riscv-iommu.h" + +#define DMA_LEN 4 + +/* RISC-V virt machine PCI configuration */ +#define RISCV_GPEX_PIO_BASE 0x3000000 +#define RISCV_BUS_PIO_LIMIT 0x10000 +#define RISCV_BUS_MMIO_ALLOC_PTR 0x40000000 +#define RISCV_BUS_MMIO_LIMIT 0x80000000 +#define RISCV_ECAM_ALLOC_PTR 0x30000000 + +typedef struct RiscvIommuTestState { + QTestState *qts; + QGenericPCIBus gbus; + QPCIDevice *iommu_dev; + QPCIDevice *testdev; + QPCIBar testdev_bar; + uint64_t iommu_base; +} RiscvIommuTestState; + +static void riscv_config_qpci_bus(QGenericPCIBus *qpci) +{ + qpci->gpex_pio_base =3D RISCV_GPEX_PIO_BASE; + qpci->bus.pio_limit =3D RISCV_BUS_PIO_LIMIT; + qpci->bus.mmio_alloc_ptr =3D RISCV_BUS_MMIO_ALLOC_PTR; + qpci->bus.mmio_limit =3D RISCV_BUS_MMIO_LIMIT; + qpci->ecam_alloc_ptr =3D RISCV_ECAM_ALLOC_PTR; +} + +static uint64_t riscv_iommu_expected_gpa(uint64_t iova) +{ + return QRIOMMU_SPACE_OFFS + QRIOMMU_L2_PTE_VAL + (iova & 0xfff); +} + +static void save_fn(QPCIDevice *dev, int devfn, void *data) +{ + QPCIDevice **pdev =3D (QPCIDevice **) data; + uint16_t vendor =3D qpci_config_readw(dev, 0); + uint16_t device =3D qpci_config_readw(dev, 2); + + g_test_message("Found PCI device: vendor=3D0x%04x device=3D0x%04x devf= n=3D0x%02x", + vendor, device, devfn); + + if (!*pdev) { + *pdev =3D dev; + } +} + +static QPCIDevice *find_riscv_iommu_pci(QGenericPCIBus *gbus, + uint64_t *iommu_base) +{ + QPCIDevice *iommu_dev =3D NULL; + QPCIBar iommu_bar; + + g_test_message("Searching for riscv-iommu-pci " + "(vendor=3D0x%04x, device=3D0x%04x)", + RISCV_IOMMU_PCI_VENDOR_ID, RISCV_IOMMU_PCI_DEVICE_ID); + + qpci_device_foreach(&gbus->bus, RISCV_IOMMU_PCI_VENDOR_ID, + RISCV_IOMMU_PCI_DEVICE_ID, save_fn, &iommu_dev); + + if (!iommu_dev) { + g_test_message("riscv-iommu-pci device not found!"); + return NULL; + } + + g_test_message("Found riscv-iommu-pci at devfn=3D0x%02x", iommu_dev->d= evfn); + + qpci_device_enable(iommu_dev); + iommu_bar =3D qpci_iomap(iommu_dev, 0, NULL); + g_assert_false(iommu_bar.is_io); + + *iommu_base =3D iommu_bar.addr; + g_test_message("RISC-V IOMMU MMIO base address: 0x%lx", *iommu_base); + + return iommu_dev; +} + +static QPCIDevice *find_iommu_testdev(QGenericPCIBus *gbus, QPCIBar *bar) +{ + QPCIDevice *dev =3D NULL; + + g_test_message("Searching for iommu-testdev (vendor=3D0x%04x, device= =3D0x%04x)", + IOMMU_TESTDEV_VENDOR_ID, IOMMU_TESTDEV_DEVICE_ID); + + qpci_device_foreach(&gbus->bus, IOMMU_TESTDEV_VENDOR_ID, + IOMMU_TESTDEV_DEVICE_ID, save_fn, &dev); + g_assert(dev); + + qpci_device_enable(dev); + *bar =3D qpci_iomap(dev, 0, NULL); + g_assert_false(bar->is_io); + + return dev; +} + +static bool riscv_iommu_test_setup(RiscvIommuTestState *state) +{ + if (!qtest_has_machine("virt")) { + g_test_skip("virt machine not available"); + return false; + } + + state->qts =3D qtest_init("-machine virt,acpi=3Doff " + "-cpu max -smp 1 -m 512 -net none " + "-device riscv-iommu-pci " + "-device iommu-testdev"); + + qpci_init_generic(&state->gbus, state->qts, NULL, false); + riscv_config_qpci_bus(&state->gbus); + + state->iommu_dev =3D find_riscv_iommu_pci(&state->gbus, &state->iommu_= base); + g_assert(state->iommu_dev); + + state->testdev =3D find_iommu_testdev(&state->gbus, &state->testdev_ba= r); + g_assert(state->testdev); + + return true; +} + +static void riscv_iommu_test_teardown(RiscvIommuTestState *state) +{ + qtest_quit(state->qts); +} + +static uint64_t riscv_iommu_check(QTestState *qts, uint64_t iommu_base, + QRIOMMUTransMode mode) +{ + uint64_t cap; + uint64_t ddtp; + uint32_t cqcsr; + uint32_t fqcsr; + uint32_t pqcsr; + uint32_t fctl; + uint32_t fctl_mask; + uint32_t fctl_desired; + uint32_t igs; + + cap =3D qtest_readq(qts, iommu_base + RISCV_IOMMU_REG_CAP); + g_assert_cmpuint((uint32_t)(cap & RISCV_IOMMU_CAP_VERSION), =3D=3D, + RISCV_IOMMU_SPEC_DOT_VER); + + fctl =3D qtest_readl(qts, iommu_base + RISCV_IOMMU_REG_FCTL); + igs =3D (cap & RISCV_IOMMU_CAP_IGS) >> 28; + g_assert_cmpuint(igs, <=3D, RISCV_IOMMU_CAP_IGS_BOTH); + + fctl_mask =3D RISCV_IOMMU_FCTL_BE | RISCV_IOMMU_FCTL_WSI | + RISCV_IOMMU_FCTL_GXL; + fctl_desired =3D fctl & ~fctl_mask; + if (igs =3D=3D RISCV_IOMMU_CAP_IGS_WSI) { + fctl_desired |=3D RISCV_IOMMU_FCTL_WSI; + } + + if ((fctl & fctl_mask) !=3D (fctl_desired & fctl_mask)) { + ddtp =3D qtest_readq(qts, iommu_base + RISCV_IOMMU_REG_DDTP); + cqcsr =3D qtest_readl(qts, iommu_base + RISCV_IOMMU_REG_CQCSR); + fqcsr =3D qtest_readl(qts, iommu_base + RISCV_IOMMU_REG_FQCSR); + pqcsr =3D qtest_readl(qts, iommu_base + RISCV_IOMMU_REG_PQCSR); + + g_assert_cmpuint((uint32_t)(ddtp & RISCV_IOMMU_DDTP_MODE), =3D=3D, + RISCV_IOMMU_DDTP_MODE_OFF); + g_assert_cmpuint(cqcsr & RISCV_IOMMU_CQCSR_CQON, =3D=3D, 0); + g_assert_cmpuint(fqcsr & RISCV_IOMMU_FQCSR_FQON, =3D=3D, 0); + g_assert_cmpuint(pqcsr & RISCV_IOMMU_PQCSR_PQON, =3D=3D, 0); + + qtest_writel(qts, iommu_base + RISCV_IOMMU_REG_FCTL, fctl_desired); + fctl =3D qtest_readl(qts, iommu_base + RISCV_IOMMU_REG_FCTL); + } + + g_assert_cmpuint(fctl & fctl_mask, =3D=3D, fctl_desired & fctl_mask); + + if (mode =3D=3D QRIOMMU_TM_S_STAGE_ONLY || mode =3D=3D QRIOMMU_TM_NEST= ED) { + g_assert((cap & RISCV_IOMMU_CAP_SV39) !=3D 0); + } + if (mode =3D=3D QRIOMMU_TM_G_STAGE_ONLY || mode =3D=3D QRIOMMU_TM_NEST= ED) { + g_assert((cap & RISCV_IOMMU_CAP_SV39X4) !=3D 0); + g_assert_cmpuint(fctl & RISCV_IOMMU_FCTL_GXL, =3D=3D, 0); + } + + return cap; +} + +static void run_riscv_iommu_translation(const QRIOMMUTestConfig *cfg) +{ + RiscvIommuTestState state =3D { 0 }; + + if (!riscv_iommu_test_setup(&state)) { + return; + } + + riscv_iommu_check(state.qts, state.iommu_base, cfg->trans_mode); + + g_test_message("### RISC-V IOMMU translation mode=3D%d ###", + cfg->trans_mode); + qriommu_run_translation_case(state.qts, state.testdev, state.testdev_b= ar, + state.iommu_base, cfg); + riscv_iommu_test_teardown(&state); +} + +static void test_riscv_iommu_bare(void) +{ + QRIOMMUTestConfig cfg =3D { + .trans_mode =3D QRIOMMU_TM_BARE, + .dma_gpa =3D QRIOMMU_IOVA, + .dma_len =3D DMA_LEN, + .expected_result =3D 0, + }; + + run_riscv_iommu_translation(&cfg); +} + +static void test_riscv_iommu_s_stage_only(void) +{ + QRIOMMUTestConfig cfg =3D { + .trans_mode =3D QRIOMMU_TM_S_STAGE_ONLY, + .dma_gpa =3D riscv_iommu_expected_gpa(QRIOMMU_IOVA), + .dma_len =3D DMA_LEN, + .expected_result =3D 0, + }; + + run_riscv_iommu_translation(&cfg); +} + +static void test_riscv_iommu_g_stage_only(void) +{ + QRIOMMUTestConfig cfg =3D { + .trans_mode =3D QRIOMMU_TM_G_STAGE_ONLY, + .dma_gpa =3D riscv_iommu_expected_gpa(QRIOMMU_IOVA), + .dma_len =3D DMA_LEN, + .expected_result =3D 0, + }; + + run_riscv_iommu_translation(&cfg); +} + +static void test_riscv_iommu_nested(void) +{ + QRIOMMUTestConfig cfg =3D { + .trans_mode =3D QRIOMMU_TM_NESTED, + .dma_gpa =3D riscv_iommu_expected_gpa(QRIOMMU_IOVA), + .dma_len =3D DMA_LEN, + .expected_result =3D 0, + }; + + run_riscv_iommu_translation(&cfg); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + qtest_add_func("/iommu-testdev/translation/bare", + test_riscv_iommu_bare); + qtest_add_func("/iommu-testdev/translation/s-stage-only", + test_riscv_iommu_s_stage_only); + qtest_add_func("/iommu-testdev/translation/g-stage-only", + test_riscv_iommu_g_stage_only); + qtest_add_func("/iommu-testdev/translation/ns-nested", + test_riscv_iommu_nested); + return g_test_run(); +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index a8b09d065f..eb45e0f97d 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -286,7 +286,10 @@ qtests_riscv32 =3D \ (config_all_devices.has_key('CONFIG_SIFIVE_E_AON') ? ['sifive-e-aon-watc= hdog-test'] : []) =20 qtests_riscv64 =3D ['riscv-csr-test'] + \ - (unpack_edk2_blobs ? ['bios-tables-test'] : []) + (unpack_edk2_blobs ? ['bios-tables-test'] : []) + \ + (config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') and + config_all_devices.has_key('CONFIG_RISCV_IOMMU') ? + ['iommu-riscv-test'] : []) =20 qos_test_ss =3D ss.source_set() qos_test_ss.add( --=20 2.52.0