From nobody Sat Jul 25 13:07:21 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=1784397168; cv=none; d=zohomail.com; s=zohoarc; b=JhTtgFPpI5a0UwgaV0fWQb2tysWQAH+Vv6HHZQeFXHO6VCHghif7FYURRVqxeBNW2Pnz1GmHag2F2rc4H+p96/dkQjmo0EJsqHBPsssRKjGqof8+47YGCVoAFIDFwIQS+mNtTzIIu0kJ8wkFYQxEOAcPJheK4S4DYZHTUsrHKdA= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1784397168; 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=3uDNuslyYdBmpRzW9mOI+m+LtQ3M11xiT3tdksQ7zfU=; b=DzTwnwCIL/DWinNuy6hcGqhiIHmtpk0gfdY2NqruW6aYft5b2kNmMYeffCk3VFkq0X0zuluBAlRoJwS8fET6JUWH3ZxfrMRR8Ek9+NZtvT92FCePPH9ABYknsaFjO1RD1sYvJZg5zputnm8ULxXagJY8IvN9EFvgQKgy7WUkR3A= 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 lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1784397168638706.8301760010055; Sat, 18 Jul 2026 10:52:48 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wl9D3-0004bw-3U; Sat, 18 Jul 2026 13:52:21 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wl9D1-0004bZ-5B for qemu-devel@nongnu.org; Sat, 18 Jul 2026 13:52:19 -0400 Received: from mail-wm1-x32f.google.com ([2a00:1450:4864:20::32f]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wl9Cy-00081W-VC for qemu-devel@nongnu.org; Sat, 18 Jul 2026 13:52:18 -0400 Received: by mail-wm1-x32f.google.com with SMTP id 5b1f17b1804b1-490d6730461so10053075e9.3 for ; Sat, 18 Jul 2026 10:52:16 -0700 (PDT) Received: from AtiePC ([79.117.54.17]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-47f63e52aa4sm15220181f8f.10.2026.07.18.10.52.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 18 Jul 2026 10:52:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784397135; x=1785001935; 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:content-type; bh=3uDNuslyYdBmpRzW9mOI+m+LtQ3M11xiT3tdksQ7zfU=; b=W/1sj2t5p2oO+6/yF0eBi3BemI35UyUekanGzG5SfRx90RHdXW1b8tTUjxrnfeUCTF Yw0BoJsRJ9iUuh92a0nIq05lk42T7Q4ZSeOMUBt0VJ8iFvaleScVcvTUlDKM89xe35yU LqRZgLZ95pv+R40pn0x1tCjSzF9f0Ai3vwc0lrUBgBmNMHI0AA4dy0PDovfbzyI2iaah 8ZbdLL5jUu4oUxfEaQleObxzoW8iEbDEM1OEE9JRNhok97x8Gyxbdjluzcqlcc39XTv0 nIRRNnnctJ4PgRUfqko1z+y1M6SOdV3LHNUERampUoctj0JH4hAstBGr4+pF7ZERBGUO qFQg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784397135; x=1785001935; 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:content-type; bh=3uDNuslyYdBmpRzW9mOI+m+LtQ3M11xiT3tdksQ7zfU=; b=LIfcFklO4tKwslcNkfChY/Q1TbNwWKqQoaWzyw1wAy4EfZ192Q2pNhSO5WWRngLVP7 Qbfcu5Cx0PpP0wSvF1hodF1/vCwaC4zZN3jtBD/A5QoglBEJMQGuPH23dO2l6Z4FU0/s 4/zAYW/nkp5X1YAcOwu+buZM+dkCN192ltxRQN+mPIkSA6PkkocqeeJps4li3e52+hef BzCog/uWX7bf+C/8Iiw39JtHlZehUikPuROyc741bsASnSAdg7RE4FzIvLC8q3ruGmGx 6SP5Duo4XV3wqqSdcpwx/+CxhMGaIz62k9uKvsCC18z89OnwND7rEjP413uBmQrY6gVA kbpA== X-Forwarded-Encrypted: i=1; AHgh+Rp6bdRUDxfT0UYLm0l2wRvmwd5kbf6F8AYRdUKVLdK5auROlS8qG3zCnbnHdYI9PFSdn6cxnxCeQt6q@nongnu.org X-Gm-Message-State: AOJu0Yx7SYasY8DresvFUIbt0ovbhrK/805FivpaNOiPJ24lQCJTwJ3H Wtrg+qcx50gS7funtMzgGMvPui5HYkuFVwUz8ufCO58RXgG3ATrJA8Nc X-Gm-Gg: AfdE7clP1/Xav/OpU10uVvL8MBTRfdVncbABZDApVIV0utpVFH0/1NQ8ex5rMio12gf oj9la7Di2tGisYix0pDXRbje3+CchHroTlv8irL4SSX9Wvuk6BiCbK3aqfl+eEaZsavrmP4SyA3 aBSusBO8j7S/uyfMuC2RASqAB5thsWJZrRdGuVIPcBZ3eYRDKBa1tzpJNbg73AKviz4lRs+jM2b LvVYvVEki/SWGVf9mbxC3VoDV3sg+kbDroJMK3k8ZlLoaRT3QarHpUsCQgc2yPltARijHz6vnv0 gyegHTtEW0X85610uwzk3t/dr7vKhsfxHZ2e6m63mzWOBJ6sjcVwjlTVWWom5wENFxz+oecqupG XV93cUaNRZIvZ7/Bec7b3DAPmOSPGDisNhiqShN1pO6ExKl5/6F/eY2ktIuE5DROn1aqrrOAAVt U= X-Received: by 2002:a05:600c:1c0b:b0:493:fe47:d528 with SMTP id 5b1f17b1804b1-4954a51b6d3mr55526215e9.8.1784397134977; Sat, 18 Jul 2026 10:52:14 -0700 (PDT) From: Daniel Paziyski To: Alejandro Jimenez , "Michael S. Tsirkin" , Paolo Bonzini , Richard Henderson , Fabiano Rosas , Laurent Vivier Cc: Daniel Paziyski , Sairaj Kodilkar , qemu-devel@nongnu.org (open list:All patches CC here) Subject: [PATCH 1/2] amd_iommu: Honor DTE[IR] and DTE[IW] when DTE[Mode] is 0 Date: Sat, 18 Jul 2026 19:52:06 +0200 Message-ID: <20260718175208.118721-2-danielpaziyski@gmail.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260718175208.118721-1-danielpaziyski@gmail.com> References: <20260718175208.118721-1-danielpaziyski@gmail.com> 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=lists1p.gnu.org; Received-SPF: pass client-ip=2a00:1450:4864:20::32f; envelope-from=danielpaziyski@gmail.com; helo=mail-wm1-x32f.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: 1784397170958158500 Content-Type: text/plain; charset="utf-8" In an AMD IOMMU's Device Table Entry (DTE), the Mode field specifies the nu= mber of levels that the page table hierarchy for translating Guest Physical Addr= esses (GPAs) to System Physical Addresses (SPAs) will have. When the DTE is valid= and translation is active (V =3D 1, TV =3D 1), and Mode is equal to 0, then the= IOMMU will act as a "filter": GPAs are the same as SPAs, but, whether reads or wr= ites are allowed depends on the values of I/O read permission (IR) and I/O write permission (IW) in the DTE. In the current code, the values of IR and IW are considered only when trave= rsing the page tables for translating GPAs to SPAs when Mode is not 0 (and are AN= Ded with their counterparts in the page tables to determine the final permissio= n), and when the DTE for a device is in passthrough mode (Mode is 0), IR and IW are not taken into consideration, and access is given unconditionally, which conflicts with the specification. This patch implements honoring of the IR and IW DTE bits when Mode is 0. To= do so, both the iommu and iommu_nodma memory regions in the AMDVIAddressSpace struct are preserved, and a new field is added, passthrough_perms, which al= ong with addr_translation will determine whether the iommu or the iommu_nodma m= emory region will be used. After a DTE invalidation, passthrough_perms and addr_translation will be set accordingly with the new DTE configuration, and when it's time to switch ad= dress spaces in amdvi_switch_address_space: - Iff passthrough is desired and IR =3D 1 and IW =3D 1, use the iommu_n= odma memory region, - otherwise, use the iommu memory region. The handler for translation (amdvi_translate), which is called when the iom= mu memory region is enabled, has a fastpath which handles passthrough mode and verifies permissions. Additionally, since the DTE is fetched in the page wa= lker, and it is possible for it to have been updated concurrently, if passthrough= mode is requested, it is handled properly. Moreover, care has been taken to not = fill the IOTLB with entries handed out when passthrough is active. Also, for the sake of easier debugging, the trace_amdvi_page_fault tracepoi= nt now also takes the BDF of the device which has caused the page fault. Signed-off-by: Daniel Paziyski --- hw/i386/amd_iommu.c | 136 +++++++++++++++++++++++++++++-------------- hw/i386/trace-events | 2 +- 2 files changed, 92 insertions(+), 46 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 90252c52af..10d75a5088 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -54,6 +54,8 @@ struct AMDVIAddressSpace { IOVATree *iova_tree; /* DMA address translation active */ bool addr_translation; + /* Permissions for direct map when DMA address translation is not acti= ve */ + unsigned passthrough_perms; }; =20 /* AMDVI cache entry */ @@ -1162,34 +1164,16 @@ static void amdvi_switch_address_space(AMDVIAddress= Space *amdvi_as) { AMDVIState *s =3D amdvi_as->iommu_state; =20 - if (s->dma_remap && amdvi_as->addr_translation) { - /* Enabling DMA region */ - memory_region_set_enabled(&amdvi_as->iommu_nodma, false); - memory_region_set_enabled(MEMORY_REGION(&amdvi_as->iommu), true); - } else { - /* Disabling DMA region, using passthrough */ - memory_region_set_enabled(MEMORY_REGION(&amdvi_as->iommu), false); - memory_region_set_enabled(&amdvi_as->iommu_nodma, true); + if (!(s->dma_remap && amdvi_as->addr_translation)) { + if (amdvi_as->passthrough_perms =3D=3D (AMDVI_PERM_READ | AMDVI_PE= RM_WRITE)) { + memory_region_set_enabled(MEMORY_REGION(&amdvi_as->iommu), fal= se); + memory_region_set_enabled(&amdvi_as->iommu_nodma, true); + return; + } } -} - -/* - * For all existing address spaces managed by the IOMMU, enable/disable the - * corresponding memory regions to reset the address translation mode and - * use passthrough by default. - */ -static void amdvi_reset_address_translation_all(AMDVIState *s) -{ - AMDVIAddressSpace *iommu_as; - GHashTableIter as_it; - - g_hash_table_iter_init(&as_it, s->address_spaces); =20 - while (g_hash_table_iter_next(&as_it, NULL, (void **)&iommu_as)) { - /* Use passthrough as default mode after reset */ - iommu_as->addr_translation =3D false; - amdvi_switch_address_space(iommu_as); - } + memory_region_set_enabled(&amdvi_as->iommu_nodma, false); + memory_region_set_enabled(MEMORY_REGION(&amdvi_as->iommu), true); } =20 static void enable_dma_mode(AMDVIAddressSpace *as, bool inval_current) @@ -1222,23 +1206,43 @@ static void enable_dma_mode(AMDVIAddressSpace *as, = bool inval_current) * - invalidate all existing mappings * - switch to no_dma memory region */ -static void enable_nodma_mode(AMDVIAddressSpace *as) +static void enable_nodma_mode(AMDVIAddressSpace *as, unsigned perms) { IOMMUNotifier *n; =20 - if (!as->addr_translation) { + if (!as->addr_translation && as->passthrough_perms =3D=3D perms) { /* passthrough is already active, nothing to do */ return; } =20 - as->addr_translation =3D false; IOMMU_NOTIFIER_FOREACH(n, &as->iommu) { /* Drop all mappings for the address space */ amdvi_address_space_unmap(as, n); } + + as->addr_translation =3D false; + as->passthrough_perms =3D perms; amdvi_switch_address_space(as); } =20 +/* + * For all existing address spaces managed by the IOMMU, enable/disable the + * corresponding memory regions to reset the address translation mode and + * use passthrough by default. + */ +static void amdvi_reset_address_translation_all(AMDVIState *s) +{ + AMDVIAddressSpace *iommu_as; + GHashTableIter as_it; + + g_hash_table_iter_init(&as_it, s->address_spaces); + + while (g_hash_table_iter_next(&as_it, NULL, (void **)&iommu_as)) { + /* Use passthrough as default mode after reset */ + enable_nodma_mode(iommu_as, AMDVI_PERM_READ | AMDVI_PERM_WRITE); + } +} + /* * A guest driver must issue the INVALIDATE_DEVTAB_ENTRY command to the IO= MMU * after changing a Device Table entry. We can use this fact to detect whe= n a @@ -1252,6 +1256,7 @@ static void amdvi_update_addr_translation_mode(AMDVIS= tate *s, uint16_t devid) AMDVIAddressSpace *as; uint64_t dte[4] =3D { 0 }; int ret; + unsigned perms; =20 as =3D amdvi_get_as_by_devid(s, devid); if (!as) { @@ -1268,14 +1273,16 @@ static void amdvi_update_addr_translation_mode(AMDV= IState *s, uint16_t devid) case 0: /* DTE was successfully retrieved */ if (!dte_mode) { - enable_nodma_mode(as); /* DTE[V]=3D1 && DTE[Mode]=3D0 =3D> pas= sthrough */ + /* DTE[V]=3D1 && DTE[Mode]=3D0 =3D> passthrough */ + perms =3D amdvi_get_perms(dte[0]); + enable_nodma_mode(as, perms); } else { enable_dma_mode(as, false); /* Enable DMA translation */ } break; case -AMDVI_FR_DTE_V: /* DTE[V]=3D0, address is passed untranslated */ - enable_nodma_mode(as); + enable_nodma_mode(as, AMDVI_PERM_READ | AMDVI_PERM_WRITE); break; case -AMDVI_FR_DTE_RTR_ERR: case -AMDVI_FR_DTE_TV: @@ -1889,15 +1896,45 @@ static void amdvi_mmio_write(void *opaque, hwaddr a= ddr, uint64_t val, } } =20 +static void amdvi_fill_iotlb_entry_passthrough(IOMMUTLBEntry *entry, + AMDVIAddressSpace *as, + unsigned passthrough_perms, + hwaddr addr, + IOMMUAccessFlags flag) +{ + unsigned perms =3D 0; + + if (flag & IOMMU_RO) { + perms |=3D AMDVI_PERM_READ; + } + if (flag & IOMMU_WO) { + perms |=3D AMDVI_PERM_WRITE; + } + + if ((passthrough_perms & perms) !=3D perms) { + amdvi_page_fault(as->iommu_state, as->devfn, addr, perms); + trace_amdvi_page_fault(addr, pci_bus_num(as->bus), PCI_SLOT(as->de= vfn), + PCI_FUNC(as->devfn), perms); + return; + } + + entry->iova =3D addr & AMDVI_PAGE_MASK_4K; + entry->translated_addr =3D addr & AMDVI_PAGE_MASK_4K; + entry->addr_mask =3D ~AMDVI_PAGE_MASK_4K; + entry->perm =3D flag & IOMMU_RW; +} + static void amdvi_page_walk(AMDVIAddressSpace *as, uint64_t *dte, IOMMUTLBEntry *ret, unsigned perms, - hwaddr addr) + hwaddr addr, bool *update_iotlb) { hwaddr page_mask, pagesize =3D 0; uint8_t mode; uint64_t pte; int fetch_ret; =20 + *update_iotlb =3D false; + /* make sure the DTE has TV =3D 1 */ if (!(dte[0] & AMDVI_DEV_TRANSLATION_VALID)) { /* @@ -1916,7 +1953,9 @@ static void amdvi_page_walk(AMDVIAddressSpace *as, ui= nt64_t *dte, return; } if (mode =3D=3D 0) { - goto no_remap; + amdvi_fill_iotlb_entry_passthrough(ret, as, amdvi_get_perms(dte[0]= ), + addr, perms =3D=3D AMDVI_PERM_WRITE ? IOMMU_WO : IOMMU_RO); + return; } =20 /* Attempt to fetch the PTE to determine if a valid mapping exists */ @@ -1931,7 +1970,8 @@ static void amdvi_page_walk(AMDVIAddressSpace *as, ui= nt64_t *dte, perms !=3D (perms & amdvi_get_perms(pte))) { =20 amdvi_page_fault(as->iommu_state, as->devfn, addr, perms); - trace_amdvi_page_fault(addr); + trace_amdvi_page_fault(addr, pci_bus_num(as->bus), PCI_SLOT(as->de= vfn), + PCI_FUNC(as->devfn), perms); return; } =20 @@ -1944,13 +1984,7 @@ static void amdvi_page_walk(AMDVIAddressSpace *as, u= int64_t *dte, ret->translated_addr =3D (pte & AMDVI_DEV_PT_ROOT_MASK) & page_mask; ret->addr_mask =3D ~page_mask; ret->perm =3D amdvi_get_perms(pte); - return; - -no_remap: - ret->iova =3D addr & AMDVI_PAGE_MASK_4K; - ret->translated_addr =3D addr & AMDVI_PAGE_MASK_4K; - ret->addr_mask =3D ~AMDVI_PAGE_MASK_4K; - ret->perm =3D amdvi_get_perms(dte[0]); + *update_iotlb =3D true; } =20 static void amdvi_do_translate(AMDVIAddressSpace *as, hwaddr addr, @@ -1961,6 +1995,7 @@ static void amdvi_do_translate(AMDVIAddressSpace *as,= hwaddr addr, AMDVIIOTLBEntry *iotlb_entry =3D amdvi_iotlb_lookup(s, addr, devid); uint64_t entry[4]; int dte_ret; + bool update_iotlb; =20 if (iotlb_entry) { trace_amdvi_iotlb_hit(PCI_BUS_NUM(devid), PCI_SLOT(devid), @@ -1983,10 +2018,14 @@ static void amdvi_do_translate(AMDVIAddressSpace *a= s, hwaddr addr, } =20 amdvi_page_walk(as, entry, ret, - is_write ? AMDVI_PERM_WRITE : AMDVI_PERM_READ, addr); + is_write ? AMDVI_PERM_WRITE : AMDVI_PERM_READ, addr, + &update_iotlb); + + if (update_iotlb) { + amdvi_update_iotlb(s, devid, addr, *ret, + entry[1] & AMDVI_DEV_DOMID_ID_MASK); + } =20 - amdvi_update_iotlb(s, devid, addr, *ret, - entry[1] & AMDVI_DEV_DOMID_ID_MASK); return; =20 out: @@ -2031,7 +2070,13 @@ static IOMMUTLBEntry amdvi_translate(IOMMUMemoryRegi= on *iommu, hwaddr addr, return ret; } =20 - amdvi_do_translate(as, addr, flag & IOMMU_WO, &ret); + if (!as->addr_translation) { + amdvi_fill_iotlb_entry_passthrough(&ret, as, as->passthrough_perms, + addr, flag); + } else { + amdvi_do_translate(as, addr, flag & IOMMU_WO, &ret); + } + trace_amdvi_translation_result(pci_bus_num(as->bus), PCI_SLOT(as->devf= n), PCI_FUNC(as->devfn), addr, ret.translated_addr); return ret; @@ -2423,6 +2468,7 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus= , void *opaque, int devfn) amdvi_dev_as->notifier_flags =3D IOMMU_NOTIFIER_NONE; amdvi_dev_as->iova_tree =3D iova_tree_new(); amdvi_dev_as->addr_translation =3D false; + amdvi_dev_as->passthrough_perms =3D AMDVI_PERM_READ | AMDVI_PERM_W= RITE; key->bus =3D bus; key->devfn =3D devfn; =20 diff --git a/hw/i386/trace-events b/hw/i386/trace-events index a1a50d0910..df2d046000 100644 --- a/hw/i386/trace-events +++ b/hw/i386/trace-events @@ -103,7 +103,7 @@ amdvi_dte_get_fail(uint64_t addr, uint32_t offset) "err= or: failed to access Devi amdvi_invalid_dte(uint64_t addr) "PTE entry at 0x%"PRIx64" is invalid " amdvi_get_pte_hwerror(uint64_t addr) "hardware error eccessing PTE at addr= 0x%"PRIx64 amdvi_mode_invalid(uint8_t level, uint64_t addr)"error: translation level = 0x%"PRIx8" translating addr 0x%"PRIx64 -amdvi_page_fault(uint64_t addr) "error: page fault accessing guest physica= l address 0x%"PRIx64 +amdvi_page_fault(uint64_t addr, uint8_t bus, uint8_t slot, uint8_t func, u= nsigned perms) "error: page fault accessing guest physical address 0x%"PRIx= 64" by devid %02x:%02x.%x with perms 0x%x" amdvi_iotlb_hit(uint8_t bus, uint8_t slot, uint8_t func, uint64_t addr, ui= nt64_t txaddr) "hit iotlb devid %02x:%02x.%x gpa 0x%"PRIx64" hpa 0x%"PRIx64 amdvi_translation_result(uint8_t bus, uint8_t slot, uint8_t func, uint64_t= addr, uint64_t txaddr) "devid: %02x:%02x.%x gpa 0x%"PRIx64" hpa 0x%"PRIx64 amdvi_mem_ir_write_req(uint64_t addr, uint64_t val, uint32_t size) "addr 0= x%"PRIx64" data 0x%"PRIx64" size 0x%"PRIx32 --=20 2.55.0 From nobody Sat Jul 25 13:07:21 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=1784397170; cv=none; d=zohomail.com; s=zohoarc; b=NSXvxebUMrq75tfWlY49aT/h1Dm40suFaWXt2cJhl1PEMdq83EZf68VN2PtDMSAoqkQ+cG0ThLj1Tt+frJzuszz2a5elx7EAydgMdkREfwDdS/Of15ULZ1hCREqpa8zVT39fd7pPFBD5q2917uQ+VKP4AvcDqfZNttYS6PdPCSs= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1784397170; 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=dDCJ20hyaBwLFUDMgdk7nW9nfTiRjuME6/CJfovCLnc=; b=EMWEPCTBJtGTUAsLxH+oJqy8FmNDdW81sG3yfSokMAaROtFgPHNo5uJ6QGyORzy8jv+9qGz0o61rLU31yQ/lAyVQBvCgcbz07nDy9RHqyiiUc78d/BYqhHfyQP0/PcsxPn76s8iYFAd3TVxaO8oTMM1d0SXe/QBImIqZvRLrJLE= 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 lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 17843971670601016.0163333756178; Sat, 18 Jul 2026 10:52:47 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wl9D4-0004cE-Km; Sat, 18 Jul 2026 13:52:23 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wl9D2-0004bl-RZ for qemu-devel@nongnu.org; Sat, 18 Jul 2026 13:52:20 -0400 Received: from mail-wm1-x32d.google.com ([2a00:1450:4864:20::32d]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wl9D0-00081g-Sl for qemu-devel@nongnu.org; Sat, 18 Jul 2026 13:52:20 -0400 Received: by mail-wm1-x32d.google.com with SMTP id 5b1f17b1804b1-493bd302c91so3658765e9.3 for ; Sat, 18 Jul 2026 10:52:18 -0700 (PDT) Received: from AtiePC ([79.117.54.17]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-47f63e52aa4sm15220181f8f.10.2026.07.18.10.52.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 18 Jul 2026 10:52:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784397137; x=1785001937; 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:content-type; bh=dDCJ20hyaBwLFUDMgdk7nW9nfTiRjuME6/CJfovCLnc=; b=V73umKaovWN3SEl3camgLGMKUjHUEArLte7/cJ6BWPuooxB4kfQqyLEsmUOy+rgGb+ 55jQqaiXbeb2P3B+u668NcsAtTnJ6fb252YSC88q6GmN/w+wa9DRdkSEtN/QXM3UiRMJ w1NSUhgFQPqMyFYrMpcMoLXL96NUTvng15lVtqMEUX9o0qe+S3xXTTwnMwNAX0fIHQ2+ zrG4yGbYJEMHuGVJf3C9nkPjagUkyHS8ZJWp2Jlv0lfI7/5ZXa1x2xyY3MNf7czEG7JK 3jpVPZdESrUNOj7szf/hPg6qyFa2YM9aq+tIYUijbFMepZA/IAoB5FvK42cueyNpUIqa Y7nw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784397137; x=1785001937; 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:content-type; bh=dDCJ20hyaBwLFUDMgdk7nW9nfTiRjuME6/CJfovCLnc=; b=Tn6tFqojL7jlJ49bpkSRzfA3bVg+6Sry+3pc92YPtLkGfXL3wVunrJc+4BzdK1lwTX DzDfyCZTwxdFMPi2masI99SDLpM3Z2IV0Aq8r1PsU7/1soh+yDHDFT6x32dNvjNJDpbv zO06KxQte1KOz66zHElzDhNE1ibvzu8MgLiUxMQzpMH18jR+JeRu/ccE1wyFhcA0mMbe RbErh6HaJQMBmw8zeFcQ3+2Y+S6slY37m0+j+tdP8RYy+bTs7JHFEWQvDXsdo2vrsCjo 9Z0O7rWy3VQXhoh4w3o12VQ3IDhINfBCt6zpnzYJov5GU0H8Y2xYBHN+WZS/WN1YbtQW 3+eQ== X-Forwarded-Encrypted: i=1; AHgh+Rp8wl9k9kq+X+lmU7SaX3W8TeqV0jK6HKvO7/4GCPz2z5GEuPDCfQ6Q34YhIrnSO3M+xs5pTuDNbe3H@nongnu.org X-Gm-Message-State: AOJu0Yx8818lzMhYMKFqBlqvdKeUswe+1kupcxryNeAwdTz1tKkOtAuy QQgbpE2Dgx+jsQYd2CxdREWNrKw0QZuWyQf5aSGkPLMJGDbcnoDNihlP X-Gm-Gg: AfdE7clQYrBQjxa3jtHzKVWmER1ZpCiD3yoEzzsKcze69rxanmzW6Okd8M9jttNaRub Et8SihgTEWNGg9h8Xjr36zDv/qnc+UE+yMeuR565/nBS5QoJ04rvCb8qBZYoCIEQru8dPTt9Dwh Ic/VWgmZog0UqKVcRVWKr8vP/hjeKTo/3+/OnYxBCxrHlTxio+5oYFV5pQtOSheJ+9ULk/9CE0d UJSEJEGyFcH05s90Q9GpqbRzvjqTDFyGhvfN+39k+qWN6ISun+c0yUGUDWnco8eOUyUo3HBAGUO PR/8tLQEVjNewgdwjMCZPeOc8X7mm5ektlB/KMb+pebFsuW6g3kk3gY1A1F819JPXKeB9IMrsDB fuEGpJZ5QUDM9fSTa3bxrP6aeZ4Hiim8XVOFTk/WQL7oNqfz+LReLSrOC8O44IaqYpzdkT9WeZd 4= X-Received: by 2002:a05:600c:35c8:b0:495:4505:dad0 with SMTP id 5b1f17b1804b1-4954a3eb076mr70047345e9.2.1784397137231; Sat, 18 Jul 2026 10:52:17 -0700 (PDT) From: Daniel Paziyski To: Alejandro Jimenez , "Michael S. Tsirkin" , Paolo Bonzini , Richard Henderson , Fabiano Rosas , Laurent Vivier Cc: Daniel Paziyski , Sairaj Kodilkar , qemu-devel@nongnu.org (open list:All patches CC here) Subject: [PATCH 2/2] tests/qtest: Add amd-iommu passthrough mode test Date: Sat, 18 Jul 2026 19:52:07 +0200 Message-ID: <20260718175208.118721-3-danielpaziyski@gmail.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260718175208.118721-1-danielpaziyski@gmail.com> References: <20260718175208.118721-1-danielpaziyski@gmail.com> 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=lists1p.gnu.org; Received-SPF: pass client-ip=2a00:1450:4864:20::32d; envelope-from=danielpaziyski@gmail.com; helo=mail-wm1-x32d.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: 1784397170836158500 Content-Type: text/plain; charset="utf-8" Test whether the AMD IOMMU behaves correctly when a device is in passthrough mode: that is, DTE[V] =3D 1, DTE[TV] =3D 1, and DTE[IR] and DTE[IW] are some combination of 0 and 1. The edu device is used as an initiator of DMA transactions. We are looking for, in order: - when IR =3D 1 and IW =3D 1, no IO page faults - when IR =3D 1 and IW =3D 0, an IO page fault on device write - when IR =3D 0 and IW =3D 1, an IO page fault on read - when IR =3D 1 and IW =3D 1, no IO page fault (back to nodma region) - when IR =3D 0 and IV =3D 0, an IO page fault on device read/write - after reset, the device to behave as if IR =3D 1 and IW =3D 1 (nodma) Signed-off-by: Daniel Paziyski --- tests/qtest/amd-iommu-test.c | 230 +++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) diff --git a/tests/qtest/amd-iommu-test.c b/tests/qtest/amd-iommu-test.c index fb28511588..79a65d9cbf 100644 --- a/tests/qtest/amd-iommu-test.c +++ b/tests/qtest/amd-iommu-test.c @@ -4,11 +4,23 @@ =20 #include "qemu/osdep.h" #include "libqtest.h" +#include "libqos/pci-pc.h" #include "hw/i386/amd_iommu.h" =20 #define CMDBUF_ADDR 0x200000 #define CMDBUF_LEN_FIELD 8 #define CMDBUF_ENTRIES (1U << CMDBUF_LEN_FIELD) +#define CMDBUF_SIZE (CMDBUF_ENTRIES * AMDVI_COMMAND_SIZE) + +#define DEVTAB_ADDR (CMDBUF_ADDR + CMDBUF_SIZE) +#define DEVTAB_LEN_FIELD 0x1ff +#define DEVTAB_ENTRIES (((DEVTAB_LEN_FIELD + 1) << 12U) / AMDVI_DEVTAB_= ENTRY_SIZE) +#define DEVTAB_SIZE ((DEVTAB_LEN_FIELD + 1) << 12U) + +#define EVLOG_ADDR (DEVTAB_ADDR + DEVTAB_SIZE) +#define EVLOG_LEN_FIELD 0x0f +#define EVLOG_ENTRIES ((4096U << (EVLOG_LEN_FIELD - 8)) / AMDVI_EVENT_= LEN) +#define EVLOG_SIZE (4096U << (EVLOG_LEN_FIELD - 8)) =20 static inline uint64_t amdvi_reg_readq(QTestState *s, uint64_t offset) { @@ -21,6 +33,34 @@ static inline void amdvi_reg_writeq(QTestState *s, uint6= 4_t offset, qtest_writeq(s, AMDVI_BASE_ADDR + offset, val); } =20 +static void amdvi_invalidate_devtab_entry(QTestState *s, uint16_t bdf) +{ + uint64_t tail =3D amdvi_reg_readq(s, AMDVI_MMIO_COMMAND_TAIL); + + qtest_writel(s, CMDBUF_ADDR + tail, bdf); + qtest_writel(s, CMDBUF_ADDR + tail + 4, 0x20000000); + qtest_writel(s, CMDBUF_ADDR + tail + 8, 0); + qtest_writel(s, CMDBUF_ADDR + tail + 12, 0); + + g_assert_true(amdvi_reg_readq(s, AMDVI_MMIO_STATUS) + & AMDVI_MMIO_STATUS_CMDBUF_RUN); + + tail =3D (tail + AMDVI_COMMAND_SIZE) % CMDBUF_SIZE; + amdvi_reg_writeq(s, AMDVI_MMIO_COMMAND_TAIL, tail); + + for (;;) { + g_assert_true(amdvi_reg_readq(s, AMDVI_MMIO_STATUS) & + AMDVI_MMIO_STATUS_CMDBUF_RUN); + if ((amdvi_reg_readq(s, AMDVI_MMIO_COMMAND_HEAD) + & AMDVI_MMIO_CMDBUF_HEAD_MASK) =3D=3D tail) { + break; + } + } + + g_assert_true(amdvi_reg_readq(s, AMDVI_MMIO_STATUS) + & AMDVI_MMIO_STATUS_CMDBUF_RUN); +} + static void test_cmdbuf_head_wrap(void) { QTestState *s; @@ -68,9 +108,199 @@ static void test_cmdbuf_head_wrap(void) qtest_quit(s); } =20 +static void test_dte_mode_0_edu_dma(QTestState *s, + QPCIDevice *edu, QPCIBar edu_bar, + hwaddr edu_dma_src, hwaddr edu_dma_dst, + bool edu_to_ram, + bool expect_fail) +{ + bool log; + uint64_t head, tail, addr; + + qpci_io_writeq(edu, edu_bar, 0x80, edu_dma_src); + qpci_io_writeq(edu, edu_bar, 0x88, edu_dma_dst); + qpci_io_writeq(edu, edu_bar, 0x90, 1); + qpci_io_writeq(edu, edu_bar, 0x98, 0x01 | (edu_to_ram ? 0x02 : 0x00)); + + while (qpci_io_readq(edu, edu_bar, 0x98) & 0x01) { + /* edu uses a timer for scheduling the DMA operation */ + qtest_clock_step_next(s); + } + + log =3D amdvi_reg_readq(s, AMDVI_MMIO_STATUS) & AMDVI_MMIO_STATUS_EVEN= T_INT; + if (expect_fail) { + g_assert_true(log); + amdvi_reg_writeq(s, AMDVI_MMIO_STATUS, AMDVI_MMIO_STATUS_EVENT_INT= ); + } else { + g_assert_false(log); + } + + if (expect_fail) { + head =3D amdvi_reg_readq(s, AMDVI_MMIO_EVENT_HEAD) & + AMDVI_MMIO_EVTLOG_HEAD_MASK; + tail =3D amdvi_reg_readq(s, AMDVI_MMIO_EVENT_TAIL) & + AMDVI_MMIO_EVTLOG_TAIL_MASK; + + /* make sure only one event has occurred */ + g_assert_true(((head + AMDVI_EVENT_LEN) % EVLOG_SIZE) =3D=3D + (tail % EVLOG_SIZE)); + + addr =3D qtest_readq(s, EVLOG_ADDR + head + 8); + + if (edu_to_ram) { + g_assert_true(addr =3D=3D edu_dma_dst); + } else { + g_assert_true(addr =3D=3D edu_dma_src); + } + + head =3D (head + AMDVI_EVENT_LEN) % EVLOG_SIZE; + amdvi_reg_writeq(s, AMDVI_MMIO_EVENT_HEAD, head); + } +} + +static void test_dte_mode_0_iommu_setup(QTestState *s) +{ + /* command buffer */ + amdvi_reg_writeq(s, AMDVI_MMIO_COMMAND_BASE, + CMDBUF_ADDR | ((uint64_t)CMDBUF_LEN_FIELD << 56)); + amdvi_reg_writeq(s, AMDVI_MMIO_COMMAND_HEAD, 0); + amdvi_reg_writeq(s, AMDVI_MMIO_COMMAND_TAIL, 0); + + /* event log */ + amdvi_reg_writeq(s, AMDVI_MMIO_EVENT_BASE, + EVLOG_ADDR | ((uint64_t)EVLOG_LEN_FIELD << 56)); + amdvi_reg_writeq(s, AMDVI_MMIO_EVENT_HEAD, 0); + amdvi_reg_writeq(s, AMDVI_MMIO_EVENT_TAIL, 0); + + /* device table */ + amdvi_reg_writeq(s, AMDVI_MMIO_DEVICE_TABLE, + DEVTAB_ADDR | DEVTAB_LEN_FIELD); + + /* enable */ + amdvi_reg_writeq(s, AMDVI_MMIO_CONTROL, AMDVI_MMIO_CONTROL_AMDVIEN | + AMDVI_MMIO_CONTROL_CMDBUFLEN | + AMDVI_MMIO_CONTROL_EVENTLOGEN | + AMDVI_MMIO_CONTROL_EVENTINTEN); +} + +static void test_dte_mode_0(void) +{ + QTestState *s; + QPCIBus *bus; + QPCIDevice *edu; + QPCIBar edu_bar; + const unsigned int edu_devfn =3D PCI_DEVFN(0x14, 0), + edu_devtab_off =3D edu_devfn * AMDVI_DEVTAB_ENTRY_S= IZE; + const hwaddr edu_dma_test_buf =3D 0x1000, edu_dma_dev_buf =3D 0x40000; + + if (!qtest_has_machine("q35")) { + g_test_skip("q35 machine not available"); + } + + if (!qtest_has_device("edu")) { + g_test_skip("edu device not available"); + } + + s =3D qtest_init("-M q35 -device amd-iommu,dma-remap=3Don,dma-translat= ion=3Don," + "device-iotlb=3Don -device edu,addr=3D14.0,dma_mask=3D0= xffffffff"); + bus =3D qpci_new_pc(s, NULL); + + test_dte_mode_0_iommu_setup(s); + + /* get bar of EDU device */ + edu =3D qpci_device_find(bus, edu_devfn); + g_assert_nonnull(edu); + + edu_bar =3D qpci_iomap(edu, 0, NULL); + qpci_device_enable(edu); + + g_test_message("testing DMA passthrough, IR =3D 1, IW =3D 1"); + qtest_writeq(s, DEVTAB_ADDR + edu_devtab_off, + AMDVI_DEV_PERM_READ | AMDVI_DEV_PERM_WRITE | + AMDVI_DEV_TRANSLATION_VALID | AMDVI_DEV_VALID); + amdvi_invalidate_devtab_entry(s, edu_devfn); + + g_test_message("testing read"); + test_dte_mode_0_edu_dma(s, edu, edu_bar, edu_dma_test_buf, edu_dma_dev= _buf, + false, false); + g_test_message("testing write"); + test_dte_mode_0_edu_dma(s, edu, edu_bar, edu_dma_dev_buf, edu_dma_test= _buf, + true, false); + + g_test_message("testing DMA passthrough, IR =3D 1, IW =3D 0"); + qtest_writeq(s, DEVTAB_ADDR + edu_devtab_off, + AMDVI_DEV_PERM_READ | + AMDVI_DEV_TRANSLATION_VALID | AMDVI_DEV_VALID); + amdvi_invalidate_devtab_entry(s, edu_devfn); + + g_test_message("testing read"); + test_dte_mode_0_edu_dma(s, edu, edu_bar, edu_dma_test_buf, edu_dma_dev= _buf, + false, false); + g_test_message("testing write"); + test_dte_mode_0_edu_dma(s, edu, edu_bar, edu_dma_dev_buf, edu_dma_test= _buf, + true, true); + + g_test_message("testing DMA passthrough, IR =3D 0, IW =3D 1"); + qtest_writeq(s, DEVTAB_ADDR + edu_devtab_off, + AMDVI_DEV_PERM_WRITE | + AMDVI_DEV_TRANSLATION_VALID | AMDVI_DEV_VALID); + amdvi_invalidate_devtab_entry(s, edu_devfn); + + g_test_message("testing read"); + test_dte_mode_0_edu_dma(s, edu, edu_bar, edu_dma_test_buf, edu_dma_dev= _buf, + false, true); + g_test_message("testing write"); + test_dte_mode_0_edu_dma(s, edu, edu_bar, edu_dma_dev_buf, edu_dma_test= _buf, + true, false); + + /* make sure the nodma memory region is activated again properly */ + g_test_message("testing DMA passthrough, IR =3D 1, IW =3D 1 (reset to = nodma)"); + qtest_writeq(s, DEVTAB_ADDR + edu_devtab_off, + AMDVI_DEV_PERM_READ | AMDVI_DEV_PERM_WRITE | + AMDVI_DEV_TRANSLATION_VALID | AMDVI_DEV_VALID); + amdvi_invalidate_devtab_entry(s, edu_devfn); + + g_test_message("testing read"); + test_dte_mode_0_edu_dma(s, edu, edu_bar, edu_dma_test_buf, edu_dma_dev= _buf, + false, false); + g_test_message("testing write"); + test_dte_mode_0_edu_dma(s, edu, edu_bar, edu_dma_dev_buf, edu_dma_test= _buf, + true, false); + + g_test_message("testing DMA passthrough, IR =3D 0, IW =3D 0"); + qtest_writeq(s, DEVTAB_ADDR + edu_devtab_off, + AMDVI_DEV_TRANSLATION_VALID | AMDVI_DEV_VALID); + amdvi_invalidate_devtab_entry(s, edu_devfn); + + g_test_message("testing read"); + test_dte_mode_0_edu_dma(s, edu, edu_bar, edu_dma_test_buf, edu_dma_dev= _buf, + false, true); + g_test_message("testing write"); + test_dte_mode_0_edu_dma(s, edu, edu_bar, edu_dma_dev_buf, edu_dma_test= _buf, + true, true); + + /* make sure that after reset, the device switches to nodma */ + qtest_system_reset(s); + test_dte_mode_0_iommu_setup(s); + + g_test_message("testing DMA after reset"); + edu_bar =3D qpci_iomap(edu, 0, NULL); + qpci_device_enable(edu); + + g_test_message("testing read"); + test_dte_mode_0_edu_dma(s, edu, edu_bar, edu_dma_test_buf, edu_dma_dev= _buf, + false, false); + g_test_message("testing write"); + test_dte_mode_0_edu_dma(s, edu, edu_bar, edu_dma_dev_buf, edu_dma_test= _buf, + true, false); + + qtest_quit(s); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); qtest_add_func("/q35/amd-iommu/cmdbuf-head-wrap", test_cmdbuf_head_wra= p); + qtest_add_func("/q35/amd-iommu/dte-mode-0", test_dte_mode_0); return g_test_run(); } --=20 2.55.0