From nobody Mon Feb 9 14:37:41 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=1770128911; cv=none; d=zohomail.com; s=zohoarc; b=nUiGONm+K/AuSXWkSvn/S2jmgESFeUjr142Qp32z4Fg+OvTaEiOzLL71TqMJjLS1zOLj1zqVaJMrBankPjp61WrLDNYxeJoHeD5ou76G/nEY3ivjpbLltlesuYx83UtPHPh2ZWOKz+mwpKJOyiiDm6yysjeRoT/1jiLv4ylNWy8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1770128911; 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=whvSwBVuYDo75Jw/Rz6Rbmf3DsAhFVAGZiscc3hmp5E=; b=f4hfx/t/kgyava30i8pGFaXDGECC8Anmksgyjpkiabyj8QwJKxXXmTp68Au84c7NpPVRnt/1SFdPxR68iLgSYRYTbpZ79q4Q2kNGzlkwbfCWDBt26/k7sgggU6w4tHIwU8HfKD24PXrDRpuzc3GeQnQ1NJiDBNzLCrJ1G/CnH78= 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 1770128911763765.6069909239077; Tue, 3 Feb 2026 06:28:31 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vnHNn-0002Ar-TI; Tue, 03 Feb 2026 09:28:00 -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 1vnHNd-00023P-5B for qemu-devel@nongnu.org; Tue, 03 Feb 2026 09:27:50 -0500 Received: from mail-dy1-x1341.google.com ([2607:f8b0:4864:20::1341]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1vnHNa-0004TO-8o for qemu-devel@nongnu.org; Tue, 03 Feb 2026 09:27:48 -0500 Received: by mail-dy1-x1341.google.com with SMTP id 5a478bee46e88-2b74f839bdfso555527eec.1 for ; Tue, 03 Feb 2026 06:27:45 -0800 (PST) Received: from ZEVORN-PC.bbrouter ([38.95.120.198]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-2b7a1af898asm24194529eec.33.2026.02.03.06.27.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 03 Feb 2026 06:27:38 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770128864; x=1770733664; 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=whvSwBVuYDo75Jw/Rz6Rbmf3DsAhFVAGZiscc3hmp5E=; b=T46ZcvD6vv4sg5ds17aqKn5p3NXdgjm7n30P7A3RygpdQeOLAdzJuMfyZnPiOFvvEl GA0RMP9NfueEfUHx6I1J/pGB/2lNmvYetkaynCgIPAn5W7q3dTEVd2Wc2E9hqZiIzRM4 Mer5smPtL5yL7NEqMt5em2YTdCBhIYAI0HmWv2GJpNXBfO+kYfAvcByUz1xtHl7k4b2/ f6mw7UyM1tSiVgW+rHYZ3HK+Z8PCvNOBwgOdYnlhKRe7gRtM3JrpD1U4LyGWxTkErBS0 gpacc2y25AFeakaCnyOWNv+dkMY2YZrJy1fvNYC9UwjD8AnsO21/lEXuLtgTuHlmk92A jIyg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770128864; x=1770733664; 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=whvSwBVuYDo75Jw/Rz6Rbmf3DsAhFVAGZiscc3hmp5E=; b=blgbIx0FgZiy5qN8sSbAbcK3fM9Yo7+fSVeWraPIM7pZZ2IVPR8EKRPxABaFmFHRdd TPdqxJchsA9/LBx4mrKR9FcCH8sAcfDnu4mh+nT17oDhzQ5XJdtOux1wwjLBq393sh+e 4Bpt9aOoHvfON+iSphUXOTrnvUR/KY6KMblplawxuiXVfMyGk2+vOUMEMoB0NVj5sBLl UYM5vEvpMEVl79QwwT1HatndzFXZwef+Z/TNVoCBvFyTMc0pdlxTXK52MloDGu+ePIz0 u2M2HVDHHWDd2uf2kf8llBqC62gYjdCSgzJBJPlDaIbgiszi/ZGk2X5X9xgFamcLAziq O9FA== X-Gm-Message-State: AOJu0Yx1kMrOpMnjmHMB4KbWTtMcUoyykAnKWpK2Mx39hfiqnSSA5cD5 idKeehGdt9mva3ayGXXYIzdalkGuc0obIlISJR13TE4Vm7n4wNrYaaji X-Gm-Gg: AZuq6aL755HQONDcLTQVJJfYpUpguhCvRvX0j63AQ4BSYZ6Liocy9Q3AH8QLVHqeqXl 1LNAmmfmkYj6GBatdtH5HnogFRCk2kheHwqp3U0dRFjh5vP4uuWc+Av+Dyj9/TMUR0rNayYEm0G 1sSqNS1hgvCy5vrba0dPpumJLiU1CEu72yOtxxzgIQwi11gBi65jvf3pxXRLpFTiQx/J6wl8ylP MiXGuk8yFa1jNVR3C5uDmyXeRGbsor1OBRVsdZwcxTqTi6pte3DJNd3h9rM/qs2Iu+tzbzc6qFM MUWIU/807tNvdx4KXi35O/Ne2I1hbhjwreiu+lrOVnDi6weYeLFe1aXe0Jk0nmmluJhqX5G4yxK aS+kVD1vX93S0kNLEbS9nv+8M5UXVxC7sY7l0aKDgSrtecHpHmUNCS4PocbUVOzsd/pHO7xJqC8 8M4fqKpYARt3x7sb5XUkEOszetuXbutnZItbxf6DS0aCp8iXPMX9ioiq4NbAv24SyH/YJBpsXd0 CnnQY5QhBvAF68qVc+Mk86j/g== X-Received: by 2002:a05:693c:4082:b0:2b8:27ec:b2b9 with SMTP id 5a478bee46e88-2b827ecb86fmr843749eec.20.1770128859255; Tue, 03 Feb 2026 06:27:39 -0800 (PST) From: chao.liu.zevorn@gmail.com 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: [PATCH v3 2/2] tests/qtest: Add RISC-V IOMMU bare-metal test Date: Tue, 3 Feb 2026 22:27:06 +0800 Message-ID: <35f046c8d21aa6d5f9a531258762e01be198d8cf.1770127918.git.chao.liu.zevorn@gmail.com> X-Mailer: git-send-email 2.53.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::1341; envelope-from=chao.liu.zevorn@gmail.com; helo=mail-dy1-x1341.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=unavailable 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: 1770128912506158500 Content-Type: text/plain; charset="utf-8" From: Chao Liu 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: Tao Tang Reviewed-by: Fabiano Rosas 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 830f56376b..73daaad841 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -349,6 +349,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..2638024891 --- /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 "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%" PRIx64, *iommu_ba= se); + + 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) +{ + g_free(state->iommu_dev); + g_free(state->testdev); + 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 dfb83650c6..25fdbc7980 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -287,7 +287,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.53.0