Re: [RFC PATCH v2 08/18] iommu/riscv: Use MSI table to enable IMSIC access

Andrew Jones
2025-09-23T14:37:34Z

On Tue, Sep 23, 2025 at 12:12:52PM +0200, Thomas Gleixner wrote:
> On Mon, Sep 22 2025 at 20:56, Jason Gunthorpe wrote:
> > On Mon, Sep 22, 2025 at 04:20:43PM -0500, Andrew Jones wrote:
> >> > It has to do with each PCI BDF having a unique set of
> >> > validation/mapping tables for MSIs that are granular to the interrupt
> >> > number.
> >>
> >> Interrupt numbers (MSI data) aren't used by the RISC-V IOMMU in any way.
> >
> > Interrupt number is a Linux concept, HW decodes the addr/data pair and
> > delivers it to some Linux interrupt. Linux doesn't care how the HW
> > treats the addr/data pair, it can ignore data if it wants.
>
> Let me explain this a bit deeper.
>
> As you said, the interrupt number is a pure kernel software construct,
> which is mapped to a hardware interrupt source.
>
> The interrupt domain, which is associated to a hardware interrupt
> source, creates the mapping and supplies the resulting configuration to
> the hardware, so that the hardware is able to raise an interrupt in the
> CPU.
>
> In case of MSI, this configuration is the MSI message (address,
> data). That's composed by the domain according to the requirements of
> the underlying CPU hardware resource. This underlying hardware resource
> can be the CPUs interrupt controller itself or some intermediary
> hardware entity.
>
> The kernel reflects this in the interrupt domain hierarchy. The simplest
> case for MSI is:
>
> [ CPU domain ] --- [ MSI domain ] -- device
>
> The flow is as follows:
>
> device driver allocates an MSI interrupt in the MSI domain
>
> MSI domain allocates an interrupt in the CPU domain
>
> CPU domain allocates an interrupt vector and composes the
> address/data pair. If @data is written to @address, the interrupt is
> raised in the CPU
>
> MSI domain converts the address/data pair into device format and
> writes it into the device.
>
> When the device fires an interrupt it writes @data to @address, which
> raises the interrupt in the CPU at the allocated CPU vector. That
> vector is then translated to the Linux interrupt number in the
> interrupt handling entry code by looking it up in the CPU domain.
>
> With a remapping domain intermediary this looks like this:
>
> [ CPU domain ] --- [ Remap domain] --- [ MSI domain ] -- device
>
> device driver allocates an MSI interrupt in the MSI domain
>
> MSI domain allocates an interrupt in the Remap domain
>
> Remap domain allocates a resource in the remap space, e.g. an entry
> in the remap translation table and then allocates an interrupt in the
> CPU domain.
>
> CPU domain allocates an interrupt vector and composes the
> address/data pair. If @data is written to @address, the interrupt is
> raised in the CPU
>
> Remap domain converts the CPU address/data pair to remap table format
> and writes it to the alloacted entry in that table. It then composes
> a new address/data pair, which points at the remap table entry.
>
> MSI domain converts the remap address/data pair into device format
> and writes it into the device.
>
> So when the device fires an interrupt it writes @data to @address,
> which triggers the remap unit. The remap unit validates that the
> address/data pair is valid for the device and if so it writes the CPU
> address/data pair, which raises the interrupt in the CPU at the
> allocated vector. That vector is then translated to the Linux
> interrupt number in the interrupt handling entry code by looking it
> up in the CPU domain.
>
> So from a kernel POV, the address/data pairs are just opaque
> configuration values, which are written into the remap table and the
> device. Whether the content of @data is relevant or not, is a hardware
> implementation detail. That implementation detail is only relevant for
> the interrupt domain code, which handle a specific part of the
> hierarchy.
>
> The MSI domain does not need to know anything about the content and the
> meaning of @address and @data. It just cares about converting that into
> the device specific storage format.
>
> The Remap domain does not need to know anything about the content and
> the meaning of the CPU domain provided @address and @data. It just cares
> about converting that into the remap table specific format.
>
> The hardware entities do not know about the Linux interrupt number at
> all. That relationship is purely software managed as a mapping from the
> allocated CPU vector to the Linux interrupt number.
>
> Hope that helps.
>

Thanks, Thomas! I always appreciate these types of detailed design
descriptions which certainly help pull all the pieces together.

So, I think I got this right, as Patch4 adds the Remap domain, creating
this hierarchy

name: IR-PCI-MSIX-0000:00:01.0-12
size: 0
mapped: 3
flags: 0x00000213
IRQ_DOMAIN_FLAG_HIERARCHY
IRQ_DOMAIN_NAME_ALLOCATED
IRQ_DOMAIN_FLAG_MSI
IRQ_DOMAIN_FLAG_MSI_DEVICE
parent: IOMMU-IR-0000:00:01.0-17
name: IOMMU-IR-0000:00:01.0-17
size: 0
mapped: 3
flags: 0x00000123
IRQ_DOMAIN_FLAG_HIERARCHY
IRQ_DOMAIN_NAME_ALLOCATED
IRQ_DOMAIN_FLAG_ISOLATED_MSI
IRQ_DOMAIN_FLAG_MSI_PARENT
parent: :soc:interrupt-controller@28000000-5
name: :soc:interrupt-controller@28000000-5
size: 0
mapped: 16
flags: 0x00000103
IRQ_DOMAIN_FLAG_HIERARCHY
IRQ_DOMAIN_NAME_ALLOCATED
IRQ_DOMAIN_FLAG_MSI_PARENT

But, Patch4 only introduces the irqdomain, the functionality is added with
Patch8. Patch8 introduces riscv_iommu_ir_get_msipte_idx_from_target()
which "converts the CPU address/data pair to remap table format". For the
RISC-V IOMMU, the data part of the pair is not used and the address
undergoes a specified translation into an index of the MSI table. For the
non-virt use case we skip the "composes a new address/data pair, which
points at the remap table entry" step since we just forward the original
with an identity mapping. For the virt case we do write a new addr,data
pair (Patch15) since we need to map guest addresses to host addresses (but
data is still just forwarded since the RISC-V IOMMU doesn't support data
remapping). The lack of data remapping is unfortunate, since the part of
the design where "The remap unit validates that the address/data pair is
valid for the device and if so it writes the CPU address/data pair" is
only half true for riscv (since the remap unit always forwards data so we
can't change it in order to implement validation of it). If we can't set
IRQ_DOMAIN_FLAG_ISOLATED_MSI without data validation, then we'll need to
try to fast-track an IOMMU extension for it before we can use VFIO without
having to set allow_unsafe_interrupts.

Thanks,
drew