[PATCH v1] hw/riscv/riscv-iommu: Avoid caching PCI device IDs

Chengbo Gao posted 1 patch 2 weeks, 2 days ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20260514020637.2819308-1-gaochengbo@bosc.ac.cn
Maintainers: Palmer Dabbelt <palmer@dabbelt.com>, Alistair Francis <alistair.francis@wdc.com>, Weiwei Li <liwei1518@gmail.com>, Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>, Liu Zhiwei <zhiwei_liu@linux.alibaba.com>, Chao Liu <chao.liu.zevorn@gmail.com>
hw/riscv/riscv-iommu.c | 40 +++++++++++++++++++++++++---------------
1 file changed, 25 insertions(+), 15 deletions(-)
[PATCH v1] hw/riscv/riscv-iommu: Avoid caching PCI device IDs
Posted by Chengbo Gao 2 weeks, 2 days ago
PCI bus numbers may still be unassigned when QEMU initializes a PCI
device's bus-master address space.  For devices behind bridges,
pci_bus_num() can return 0 at that point because the guest has not yet
programmed the bridge Secondary Bus Number register.

The RISC-V IOMMU currently stores a fixed device_id in RISCVIOMMUSpace
when the address space is created.  If the guest later enumerates the
device on a non-zero bus, DMA translation still uses the stale device_id
and may look up the wrong device context in the DDT.

Store the stable PCIBus pointer and devfn in RISCVIOMMUSpace instead,
and compute the device_id from the current bus number when it is needed.
This keeps DMA translation and ATS invalidation in sync with guest PCI
bus enumeration.

Signed-off-by: Chengbo Gao <gaochengbo@bosc.ac.cn>
---
 hw/riscv/riscv-iommu.c | 40 +++++++++++++++++++++++++---------------
 1 file changed, 25 insertions(+), 15 deletions(-)

diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c
index 7ba3240552..9a75639549 100644
--- a/hw/riscv/riscv-iommu.c
+++ b/hw/riscv/riscv-iommu.c
@@ -49,7 +49,8 @@ struct RISCVIOMMUSpace {
     IOMMUMemoryRegion iova_mr;  /* IOVA memory region for attached device */
     AddressSpace iova_as;       /* IOVA address space for attached device */
     RISCVIOMMUState *iommu;     /* Managing IOMMU device state */
-    uint32_t devid;             /* Requester identifier, AKA device_id */
+    PCIBus *bus;                /* PCI bus of the requester */
+    uint8_t devfn;              /* Requester identifier, AKA device_id */
     bool notifier;              /* IOMMU unmap notifier enabled */
     QLIST_ENTRY(RISCVIOMMUSpace) list;
 };
@@ -74,6 +75,15 @@ struct RISCVIOMMUEntry {
 /* IOMMU index for transactions without process_id specified. */
 #define RISCV_IOMMU_NOPROCID 0
 
+static uint32_t riscv_iommu_space_devid(RISCVIOMMUSpace *as)
+{
+    uint32_t devid = PCI_BUILD_BDF(pci_bus_num(as->bus), as->devfn);
+
+    /* FIXME: PCIe bus remapping for attached endpoints. */
+    devid |= as->iommu->bus << 8;
+    return devid;
+}
+
 static uint8_t riscv_iommu_get_icvec_vector(uint32_t icvec, uint32_t vec_type)
 {
     switch (vec_type) {
@@ -1343,15 +1353,13 @@ static void riscv_iommu_ctx_put(RISCVIOMMUState *s, void *ref)
 }
 
 /* Find or allocate address space for a given device */
-static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, uint32_t devid)
+static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, PCIBus *bus,
+                                            int devfn)
 {
     RISCVIOMMUSpace *as;
 
-    /* FIXME: PCIe bus remapping for attached endpoints. */
-    devid |= s->bus << 8;
-
     QLIST_FOREACH(as, &s->spaces, list) {
-        if (as->devid == devid) {
+        if (as->bus == bus && as->devfn == devfn) {
             break;
         }
     }
@@ -1361,10 +1369,11 @@ static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, uint32_t devid)
         as = g_new0(RISCVIOMMUSpace, 1);
 
         as->iommu = s;
-        as->devid = devid;
+        as->bus = bus;
+        as->devfn = devfn;
 
         snprintf(name, sizeof(name), "riscv-iommu-%04x:%02x.%d-iova",
-            PCI_BUS_NUM(as->devid), PCI_SLOT(as->devid), PCI_FUNC(as->devid));
+            pci_bus_num(bus), PCI_SLOT(devfn), PCI_FUNC(devfn));
 
         /* IOVA address space, untranslated addresses */
         memory_region_init_iommu(&as->iova_mr, sizeof(as->iova_mr),
@@ -1374,8 +1383,8 @@ static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, uint32_t devid)
 
         QLIST_INSERT_HEAD(&s->spaces, as, list);
 
-        trace_riscv_iommu_new(s->parent_obj.id, PCI_BUS_NUM(as->devid),
-                PCI_SLOT(as->devid), PCI_FUNC(as->devid));
+        trace_riscv_iommu_new(s->parent_obj.id, pci_bus_num(bus),
+                PCI_SLOT(devfn), PCI_FUNC(devfn));
     }
     return &as->iova_as;
 }
@@ -1696,7 +1705,7 @@ static void riscv_iommu_ats(RISCVIOMMUState *s,
     pid = get_field(cmd->dword0, RISCV_IOMMU_CMD_ATS_PID);
 
     QLIST_FOREACH(as, &s->spaces, list) {
-        if (as->devid == devid) {
+        if (riscv_iommu_space_devid(as) == devid) {
             break;
         }
     }
@@ -2710,8 +2719,9 @@ static IOMMUTLBEntry riscv_iommu_memory_region_translate(
         .addr_mask = ~0ULL,
         .perm = flag,
     };
+    uint32_t devid = riscv_iommu_space_devid(as);
 
-    ctx = riscv_iommu_ctx(as->iommu, as->devid, iommu_idx, &ref);
+    ctx = riscv_iommu_ctx(as->iommu, devid, iommu_idx, &ref);
     if (ctx == NULL) {
         /* Translation disabled or invalid. */
         iotlb.addr_mask = 0;
@@ -2723,8 +2733,8 @@ static IOMMUTLBEntry riscv_iommu_memory_region_translate(
     }
 
     /* Trace all dma translations with original access flags. */
-    trace_riscv_iommu_dma(as->iommu->parent_obj.id, PCI_BUS_NUM(as->devid),
-                          PCI_SLOT(as->devid), PCI_FUNC(as->devid), iommu_idx,
+    trace_riscv_iommu_dma(as->iommu->parent_obj.id, PCI_BUS_NUM(devid),
+                          PCI_SLOT(devid), PCI_FUNC(devid), iommu_idx,
                           IOMMU_FLAG_STR[flag & IOMMU_RW], iotlb.iova,
                           iotlb.translated_addr);
 
@@ -2772,7 +2782,7 @@ static AddressSpace *riscv_iommu_find_as(PCIBus *bus, void *opaque, int devfn)
 
     /* Find first matching IOMMU */
     while (s != NULL && as == NULL) {
-        as = riscv_iommu_space(s, PCI_BUILD_BDF(pci_bus_num(bus), devfn));
+        as = riscv_iommu_space(s, bus, devfn);
         s = s->iommus.le_next;
     }
 
-- 
2.34.1
Re: [PATCH v1] hw/riscv/riscv-iommu: Avoid caching PCI device IDs
Posted by Alistair Francis 1 week, 5 days ago
On Thu, May 14, 2026 at 12:09 PM Chengbo Gao <gaochengbo@bosc.ac.cn> wrote:
>
> PCI bus numbers may still be unassigned when QEMU initializes a PCI
> device's bus-master address space.  For devices behind bridges,
> pci_bus_num() can return 0 at that point because the guest has not yet
> programmed the bridge Secondary Bus Number register.
>
> The RISC-V IOMMU currently stores a fixed device_id in RISCVIOMMUSpace
> when the address space is created.  If the guest later enumerates the
> device on a non-zero bus, DMA translation still uses the stale device_id
> and may look up the wrong device context in the DDT.
>
> Store the stable PCIBus pointer and devfn in RISCVIOMMUSpace instead,
> and compute the device_id from the current bus number when it is needed.
> This keeps DMA translation and ATS invalidation in sync with guest PCI
> bus enumeration.
>
> Signed-off-by: Chengbo Gao <gaochengbo@bosc.ac.cn>

Thanks!

Applied to riscv-to-apply.next

Alistair


> ---
>  hw/riscv/riscv-iommu.c | 40 +++++++++++++++++++++++++---------------
>  1 file changed, 25 insertions(+), 15 deletions(-)
>
> diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c
> index 7ba3240552..9a75639549 100644
> --- a/hw/riscv/riscv-iommu.c
> +++ b/hw/riscv/riscv-iommu.c
> @@ -49,7 +49,8 @@ struct RISCVIOMMUSpace {
>      IOMMUMemoryRegion iova_mr;  /* IOVA memory region for attached device */
>      AddressSpace iova_as;       /* IOVA address space for attached device */
>      RISCVIOMMUState *iommu;     /* Managing IOMMU device state */
> -    uint32_t devid;             /* Requester identifier, AKA device_id */
> +    PCIBus *bus;                /* PCI bus of the requester */
> +    uint8_t devfn;              /* Requester identifier, AKA device_id */
>      bool notifier;              /* IOMMU unmap notifier enabled */
>      QLIST_ENTRY(RISCVIOMMUSpace) list;
>  };
> @@ -74,6 +75,15 @@ struct RISCVIOMMUEntry {
>  /* IOMMU index for transactions without process_id specified. */
>  #define RISCV_IOMMU_NOPROCID 0
>
> +static uint32_t riscv_iommu_space_devid(RISCVIOMMUSpace *as)
> +{
> +    uint32_t devid = PCI_BUILD_BDF(pci_bus_num(as->bus), as->devfn);
> +
> +    /* FIXME: PCIe bus remapping for attached endpoints. */
> +    devid |= as->iommu->bus << 8;
> +    return devid;
> +}
> +
>  static uint8_t riscv_iommu_get_icvec_vector(uint32_t icvec, uint32_t vec_type)
>  {
>      switch (vec_type) {
> @@ -1343,15 +1353,13 @@ static void riscv_iommu_ctx_put(RISCVIOMMUState *s, void *ref)
>  }
>
>  /* Find or allocate address space for a given device */
> -static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, uint32_t devid)
> +static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, PCIBus *bus,
> +                                            int devfn)
>  {
>      RISCVIOMMUSpace *as;
>
> -    /* FIXME: PCIe bus remapping for attached endpoints. */
> -    devid |= s->bus << 8;
> -
>      QLIST_FOREACH(as, &s->spaces, list) {
> -        if (as->devid == devid) {
> +        if (as->bus == bus && as->devfn == devfn) {
>              break;
>          }
>      }
> @@ -1361,10 +1369,11 @@ static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, uint32_t devid)
>          as = g_new0(RISCVIOMMUSpace, 1);
>
>          as->iommu = s;
> -        as->devid = devid;
> +        as->bus = bus;
> +        as->devfn = devfn;
>
>          snprintf(name, sizeof(name), "riscv-iommu-%04x:%02x.%d-iova",
> -            PCI_BUS_NUM(as->devid), PCI_SLOT(as->devid), PCI_FUNC(as->devid));
> +            pci_bus_num(bus), PCI_SLOT(devfn), PCI_FUNC(devfn));
>
>          /* IOVA address space, untranslated addresses */
>          memory_region_init_iommu(&as->iova_mr, sizeof(as->iova_mr),
> @@ -1374,8 +1383,8 @@ static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, uint32_t devid)
>
>          QLIST_INSERT_HEAD(&s->spaces, as, list);
>
> -        trace_riscv_iommu_new(s->parent_obj.id, PCI_BUS_NUM(as->devid),
> -                PCI_SLOT(as->devid), PCI_FUNC(as->devid));
> +        trace_riscv_iommu_new(s->parent_obj.id, pci_bus_num(bus),
> +                PCI_SLOT(devfn), PCI_FUNC(devfn));
>      }
>      return &as->iova_as;
>  }
> @@ -1696,7 +1705,7 @@ static void riscv_iommu_ats(RISCVIOMMUState *s,
>      pid = get_field(cmd->dword0, RISCV_IOMMU_CMD_ATS_PID);
>
>      QLIST_FOREACH(as, &s->spaces, list) {
> -        if (as->devid == devid) {
> +        if (riscv_iommu_space_devid(as) == devid) {
>              break;
>          }
>      }
> @@ -2710,8 +2719,9 @@ static IOMMUTLBEntry riscv_iommu_memory_region_translate(
>          .addr_mask = ~0ULL,
>          .perm = flag,
>      };
> +    uint32_t devid = riscv_iommu_space_devid(as);
>
> -    ctx = riscv_iommu_ctx(as->iommu, as->devid, iommu_idx, &ref);
> +    ctx = riscv_iommu_ctx(as->iommu, devid, iommu_idx, &ref);
>      if (ctx == NULL) {
>          /* Translation disabled or invalid. */
>          iotlb.addr_mask = 0;
> @@ -2723,8 +2733,8 @@ static IOMMUTLBEntry riscv_iommu_memory_region_translate(
>      }
>
>      /* Trace all dma translations with original access flags. */
> -    trace_riscv_iommu_dma(as->iommu->parent_obj.id, PCI_BUS_NUM(as->devid),
> -                          PCI_SLOT(as->devid), PCI_FUNC(as->devid), iommu_idx,
> +    trace_riscv_iommu_dma(as->iommu->parent_obj.id, PCI_BUS_NUM(devid),
> +                          PCI_SLOT(devid), PCI_FUNC(devid), iommu_idx,
>                            IOMMU_FLAG_STR[flag & IOMMU_RW], iotlb.iova,
>                            iotlb.translated_addr);
>
> @@ -2772,7 +2782,7 @@ static AddressSpace *riscv_iommu_find_as(PCIBus *bus, void *opaque, int devfn)
>
>      /* Find first matching IOMMU */
>      while (s != NULL && as == NULL) {
> -        as = riscv_iommu_space(s, PCI_BUILD_BDF(pci_bus_num(bus), devfn));
> +        as = riscv_iommu_space(s, bus, devfn);
>          s = s->iommus.le_next;
>      }
>
> --
> 2.34.1
>
>
Re: [PATCH v1] hw/riscv/riscv-iommu: Avoid caching PCI device IDs
Posted by Daniel Henrique Barboza 2 weeks, 1 day ago

On 5/13/2026 11:06 PM, Chengbo Gao wrote:
> PCI bus numbers may still be unassigned when QEMU initializes a PCI
> device's bus-master address space.  For devices behind bridges,
> pci_bus_num() can return 0 at that point because the guest has not yet
> programmed the bridge Secondary Bus Number register.
> 
> The RISC-V IOMMU currently stores a fixed device_id in RISCVIOMMUSpace
> when the address space is created.  If the guest later enumerates the
> device on a non-zero bus, DMA translation still uses the stale device_id
> and may look up the wrong device context in the DDT.
> 
> Store the stable PCIBus pointer and devfn in RISCVIOMMUSpace instead,
> and compute the device_id from the current bus number when it is needed.
> This keeps DMA translation and ATS invalidation in sync with guest PCI
> bus enumeration.
> 
> Signed-off-by: Chengbo Gao <gaochengbo@bosc.ac.cn>
> ---

Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>

>   hw/riscv/riscv-iommu.c | 40 +++++++++++++++++++++++++---------------
>   1 file changed, 25 insertions(+), 15 deletions(-)
> 
> diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c
> index 7ba3240552..9a75639549 100644
> --- a/hw/riscv/riscv-iommu.c
> +++ b/hw/riscv/riscv-iommu.c
> @@ -49,7 +49,8 @@ struct RISCVIOMMUSpace {
>       IOMMUMemoryRegion iova_mr;  /* IOVA memory region for attached device */
>       AddressSpace iova_as;       /* IOVA address space for attached device */
>       RISCVIOMMUState *iommu;     /* Managing IOMMU device state */
> -    uint32_t devid;             /* Requester identifier, AKA device_id */
> +    PCIBus *bus;                /* PCI bus of the requester */
> +    uint8_t devfn;              /* Requester identifier, AKA device_id */
>       bool notifier;              /* IOMMU unmap notifier enabled */
>       QLIST_ENTRY(RISCVIOMMUSpace) list;
>   };
> @@ -74,6 +75,15 @@ struct RISCVIOMMUEntry {
>   /* IOMMU index for transactions without process_id specified. */
>   #define RISCV_IOMMU_NOPROCID 0
>   
> +static uint32_t riscv_iommu_space_devid(RISCVIOMMUSpace *as)
> +{
> +    uint32_t devid = PCI_BUILD_BDF(pci_bus_num(as->bus), as->devfn);
> +
> +    /* FIXME: PCIe bus remapping for attached endpoints. */
> +    devid |= as->iommu->bus << 8;
> +    return devid;
> +}
> +
>   static uint8_t riscv_iommu_get_icvec_vector(uint32_t icvec, uint32_t vec_type)
>   {
>       switch (vec_type) {
> @@ -1343,15 +1353,13 @@ static void riscv_iommu_ctx_put(RISCVIOMMUState *s, void *ref)
>   }
>   
>   /* Find or allocate address space for a given device */
> -static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, uint32_t devid)
> +static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, PCIBus *bus,
> +                                            int devfn)
>   {
>       RISCVIOMMUSpace *as;
>   
> -    /* FIXME: PCIe bus remapping for attached endpoints. */
> -    devid |= s->bus << 8;
> -
>       QLIST_FOREACH(as, &s->spaces, list) {
> -        if (as->devid == devid) {
> +        if (as->bus == bus && as->devfn == devfn) {
>               break;
>           }
>       }
> @@ -1361,10 +1369,11 @@ static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, uint32_t devid)
>           as = g_new0(RISCVIOMMUSpace, 1);
>   
>           as->iommu = s;
> -        as->devid = devid;
> +        as->bus = bus;
> +        as->devfn = devfn;
>   
>           snprintf(name, sizeof(name), "riscv-iommu-%04x:%02x.%d-iova",
> -            PCI_BUS_NUM(as->devid), PCI_SLOT(as->devid), PCI_FUNC(as->devid));
> +            pci_bus_num(bus), PCI_SLOT(devfn), PCI_FUNC(devfn));
>   
>           /* IOVA address space, untranslated addresses */
>           memory_region_init_iommu(&as->iova_mr, sizeof(as->iova_mr),
> @@ -1374,8 +1383,8 @@ static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, uint32_t devid)
>   
>           QLIST_INSERT_HEAD(&s->spaces, as, list);
>   
> -        trace_riscv_iommu_new(s->parent_obj.id, PCI_BUS_NUM(as->devid),
> -                PCI_SLOT(as->devid), PCI_FUNC(as->devid));
> +        trace_riscv_iommu_new(s->parent_obj.id, pci_bus_num(bus),
> +                PCI_SLOT(devfn), PCI_FUNC(devfn));
>       }
>       return &as->iova_as;
>   }
> @@ -1696,7 +1705,7 @@ static void riscv_iommu_ats(RISCVIOMMUState *s,
>       pid = get_field(cmd->dword0, RISCV_IOMMU_CMD_ATS_PID);
>   
>       QLIST_FOREACH(as, &s->spaces, list) {
> -        if (as->devid == devid) {
> +        if (riscv_iommu_space_devid(as) == devid) {
>               break;
>           }
>       }
> @@ -2710,8 +2719,9 @@ static IOMMUTLBEntry riscv_iommu_memory_region_translate(
>           .addr_mask = ~0ULL,
>           .perm = flag,
>       };
> +    uint32_t devid = riscv_iommu_space_devid(as);
>   
> -    ctx = riscv_iommu_ctx(as->iommu, as->devid, iommu_idx, &ref);
> +    ctx = riscv_iommu_ctx(as->iommu, devid, iommu_idx, &ref);
>       if (ctx == NULL) {
>           /* Translation disabled or invalid. */
>           iotlb.addr_mask = 0;
> @@ -2723,8 +2733,8 @@ static IOMMUTLBEntry riscv_iommu_memory_region_translate(
>       }
>   
>       /* Trace all dma translations with original access flags. */
> -    trace_riscv_iommu_dma(as->iommu->parent_obj.id, PCI_BUS_NUM(as->devid),
> -                          PCI_SLOT(as->devid), PCI_FUNC(as->devid), iommu_idx,
> +    trace_riscv_iommu_dma(as->iommu->parent_obj.id, PCI_BUS_NUM(devid),
> +                          PCI_SLOT(devid), PCI_FUNC(devid), iommu_idx,
>                             IOMMU_FLAG_STR[flag & IOMMU_RW], iotlb.iova,
>                             iotlb.translated_addr);
>   
> @@ -2772,7 +2782,7 @@ static AddressSpace *riscv_iommu_find_as(PCIBus *bus, void *opaque, int devfn)
>   
>       /* Find first matching IOMMU */
>       while (s != NULL && as == NULL) {
> -        as = riscv_iommu_space(s, PCI_BUILD_BDF(pci_bus_num(bus), devfn));
> +        as = riscv_iommu_space(s, bus, devfn);
>           s = s->iommus.le_next;
>       }
>
Re: [PATCH v1] hw/riscv/riscv-iommu: Avoid caching PCI device IDs
Posted by Daniel Henrique Barboza 2 weeks, 1 day ago

On 5/13/2026 11:06 PM, Chengbo Gao wrote:
> PCI bus numbers may still be unassigned when QEMU initializes a PCI
> device's bus-master address space.  For devices behind bridges,
> pci_bus_num() can return 0 at that point because the guest has not yet
> programmed the bridge Secondary Bus Number register.
> 
> The RISC-V IOMMU currently stores a fixed device_id in RISCVIOMMUSpace
> when the address space is created.  If the guest later enumerates the
> device on a non-zero bus, DMA translation still uses the stale device_id
> and may look up the wrong device context in the DDT.
> 
> Store the stable PCIBus pointer and devfn in RISCVIOMMUSpace instead,
> and compute the device_id from the current bus number when it is needed.
> This keeps DMA translation and ATS invalidation in sync with guest PCI
> bus enumeration.
> 
> Signed-off-by: Chengbo Gao <gaochengbo@bosc.ac.cn>
> ---

Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>

>   hw/riscv/riscv-iommu.c | 40 +++++++++++++++++++++++++---------------
>   1 file changed, 25 insertions(+), 15 deletions(-)
> 
> diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c
> index 7ba3240552..9a75639549 100644
> --- a/hw/riscv/riscv-iommu.c
> +++ b/hw/riscv/riscv-iommu.c
> @@ -49,7 +49,8 @@ struct RISCVIOMMUSpace {
>       IOMMUMemoryRegion iova_mr;  /* IOVA memory region for attached device */
>       AddressSpace iova_as;       /* IOVA address space for attached device */
>       RISCVIOMMUState *iommu;     /* Managing IOMMU device state */
> -    uint32_t devid;             /* Requester identifier, AKA device_id */
> +    PCIBus *bus;                /* PCI bus of the requester */
> +    uint8_t devfn;              /* Requester identifier, AKA device_id */
>       bool notifier;              /* IOMMU unmap notifier enabled */
>       QLIST_ENTRY(RISCVIOMMUSpace) list;
>   };
> @@ -74,6 +75,15 @@ struct RISCVIOMMUEntry {
>   /* IOMMU index for transactions without process_id specified. */
>   #define RISCV_IOMMU_NOPROCID 0
>   
> +static uint32_t riscv_iommu_space_devid(RISCVIOMMUSpace *as)
> +{
> +    uint32_t devid = PCI_BUILD_BDF(pci_bus_num(as->bus), as->devfn);
> +
> +    /* FIXME: PCIe bus remapping for attached endpoints. */
> +    devid |= as->iommu->bus << 8;
> +    return devid;
> +}
> +
>   static uint8_t riscv_iommu_get_icvec_vector(uint32_t icvec, uint32_t vec_type)
>   {
>       switch (vec_type) {
> @@ -1343,15 +1353,13 @@ static void riscv_iommu_ctx_put(RISCVIOMMUState *s, void *ref)
>   }
>   
>   /* Find or allocate address space for a given device */
> -static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, uint32_t devid)
> +static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, PCIBus *bus,
> +                                            int devfn)
>   {
>       RISCVIOMMUSpace *as;
>   
> -    /* FIXME: PCIe bus remapping for attached endpoints. */
> -    devid |= s->bus << 8;
> -
>       QLIST_FOREACH(as, &s->spaces, list) {
> -        if (as->devid == devid) {
> +        if (as->bus == bus && as->devfn == devfn) {
>               break;
>           }
>       }
> @@ -1361,10 +1369,11 @@ static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, uint32_t devid)
>           as = g_new0(RISCVIOMMUSpace, 1);
>   
>           as->iommu = s;
> -        as->devid = devid;
> +        as->bus = bus;
> +        as->devfn = devfn;
>   
>           snprintf(name, sizeof(name), "riscv-iommu-%04x:%02x.%d-iova",
> -            PCI_BUS_NUM(as->devid), PCI_SLOT(as->devid), PCI_FUNC(as->devid));
> +            pci_bus_num(bus), PCI_SLOT(devfn), PCI_FUNC(devfn));
>   
>           /* IOVA address space, untranslated addresses */
>           memory_region_init_iommu(&as->iova_mr, sizeof(as->iova_mr),
> @@ -1374,8 +1383,8 @@ static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, uint32_t devid)
>   
>           QLIST_INSERT_HEAD(&s->spaces, as, list);
>   
> -        trace_riscv_iommu_new(s->parent_obj.id, PCI_BUS_NUM(as->devid),
> -                PCI_SLOT(as->devid), PCI_FUNC(as->devid));
> +        trace_riscv_iommu_new(s->parent_obj.id, pci_bus_num(bus),
> +                PCI_SLOT(devfn), PCI_FUNC(devfn));
>       }
>       return &as->iova_as;
>   }
> @@ -1696,7 +1705,7 @@ static void riscv_iommu_ats(RISCVIOMMUState *s,
>       pid = get_field(cmd->dword0, RISCV_IOMMU_CMD_ATS_PID);
>   
>       QLIST_FOREACH(as, &s->spaces, list) {
> -        if (as->devid == devid) {
> +        if (riscv_iommu_space_devid(as) == devid) {
>               break;
>           }
>       }
> @@ -2710,8 +2719,9 @@ static IOMMUTLBEntry riscv_iommu_memory_region_translate(
>           .addr_mask = ~0ULL,
>           .perm = flag,
>       };
> +    uint32_t devid = riscv_iommu_space_devid(as);
>   
> -    ctx = riscv_iommu_ctx(as->iommu, as->devid, iommu_idx, &ref);
> +    ctx = riscv_iommu_ctx(as->iommu, devid, iommu_idx, &ref);
>       if (ctx == NULL) {
>           /* Translation disabled or invalid. */
>           iotlb.addr_mask = 0;
> @@ -2723,8 +2733,8 @@ static IOMMUTLBEntry riscv_iommu_memory_region_translate(
>       }
>   
>       /* Trace all dma translations with original access flags. */
> -    trace_riscv_iommu_dma(as->iommu->parent_obj.id, PCI_BUS_NUM(as->devid),
> -                          PCI_SLOT(as->devid), PCI_FUNC(as->devid), iommu_idx,
> +    trace_riscv_iommu_dma(as->iommu->parent_obj.id, PCI_BUS_NUM(devid),
> +                          PCI_SLOT(devid), PCI_FUNC(devid), iommu_idx,
>                             IOMMU_FLAG_STR[flag & IOMMU_RW], iotlb.iova,
>                             iotlb.translated_addr);
>   
> @@ -2772,7 +2782,7 @@ static AddressSpace *riscv_iommu_find_as(PCIBus *bus, void *opaque, int devfn)
>   
>       /* Find first matching IOMMU */
>       while (s != NULL && as == NULL) {
> -        as = riscv_iommu_space(s, PCI_BUILD_BDF(pci_bus_num(bus), devfn));
> +        as = riscv_iommu_space(s, bus, devfn);
>           s = s->iommus.le_next;
>       }
>