Browse Source

intel_iommu: Support memory operations with pre-translated addresses

Signed-off-by: Clement Mathieu--Drif <clement.mathieu--drif@eviden.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-Id: <20251029105137.1097933-3-clement.mathieu--drif@eviden.com>
pull/319/head
CLEMENT MATHIEU--DRIF 9 months ago
committed by Michael S. Tsirkin
parent
commit
05f858cb67
  1. 118
      hw/i386/intel_iommu.c

118
hw/i386/intel_iommu.c

@ -3985,6 +3985,25 @@ static void vtd_mem_write(void *opaque, hwaddr addr,
}
}
static void vtd_prepare_identity_entry(hwaddr addr, IOMMUAccessFlags perm,
uint32_t pasid, IOMMUTLBEntry *iotlb)
{
iotlb->iova = addr & VTD_PAGE_MASK_4K;
iotlb->translated_addr = addr & VTD_PAGE_MASK_4K;
iotlb->addr_mask = ~VTD_PAGE_MASK_4K;
iotlb->perm = perm;
iotlb->pasid = pasid;
}
static inline void vtd_prepare_error_entry(IOMMUTLBEntry *entry)
{
entry->iova = 0;
entry->translated_addr = 0;
entry->addr_mask = ~VTD_PAGE_MASK_4K;
entry->perm = IOMMU_NONE;
entry->pasid = PCI_NO_PASID;
}
static IOMMUTLBEntry vtd_iommu_translate(IOMMUMemoryRegion *iommu, hwaddr addr,
IOMMUAccessFlags flag, int iommu_idx)
{
@ -3996,16 +4015,29 @@ static IOMMUTLBEntry vtd_iommu_translate(IOMMUMemoryRegion *iommu, hwaddr addr,
.pasid = vtd_as->pasid,
};
bool success;
bool is_write = flag & IOMMU_WO;
if (likely(s->dmar_enabled)) {
success = vtd_do_iommu_translate(vtd_as, vtd_as->bus, vtd_as->devfn,
addr, flag & IOMMU_WO, &iotlb);
/* Only support translated requests in scalable mode */
if (iommu_idx == VTD_IDX_TRANSLATED && s->root_scalable) {
if (vtd_as->pasid == PCI_NO_PASID) {
vtd_prepare_identity_entry(addr, IOMMU_RW, PCI_NO_PASID,
&iotlb);
success = true;
} else {
vtd_prepare_error_entry(&iotlb);
error_report_once("%s: translated request with PASID not "
"allowed (pasid=0x%" PRIx32 ")", __func__,
vtd_as->pasid);
success = false;
}
} else {
success = vtd_do_iommu_translate(vtd_as, vtd_as->bus, vtd_as->devfn,
addr, is_write, &iotlb);
}
} else {
/* DMAR disabled, passthrough, use 4k-page*/
iotlb.iova = addr & VTD_PAGE_MASK_4K;
iotlb.translated_addr = addr & VTD_PAGE_MASK_4K;
iotlb.addr_mask = ~VTD_PAGE_MASK_4K;
iotlb.perm = IOMMU_RW;
vtd_prepare_identity_entry(addr, IOMMU_RW, vtd_as->pasid, &iotlb);
success = true;
}
@ -4414,6 +4446,37 @@ static int vtd_int_remap(X86IOMMUState *iommu, MSIMessage *src,
src, dst, sid, false);
}
static void vtd_report_sid_ir_illegal_access(IntelIOMMUState *s, uint16_t sid,
uint32_t pasid, hwaddr addr,
bool is_write)
{
uint8_t bus_n = VTD_SID_TO_BUS(sid);
uint8_t devfn = VTD_SID_TO_DEVFN(sid);
bool is_fpd_set = false;
VTDContextEntry ce;
/* Try out best to fetch FPD, we can't do anything more */
if (vtd_dev_to_context_entry(s, bus_n, devfn, &ce) == 0) {
is_fpd_set = ce.lo & VTD_CONTEXT_ENTRY_FPD;
if (!is_fpd_set && s->root_scalable) {
vtd_ce_get_pasid_fpd(s, &ce, &is_fpd_set, pasid);
}
}
vtd_report_fault(s, VTD_FR_SM_INTERRUPT_ADDR, is_fpd_set, sid, addr,
is_write, pasid != PCI_NO_PASID, pasid);
}
static void vtd_report_ir_illegal_access(VTDAddressSpace *vtd_as,
hwaddr addr, bool is_write)
{
uint8_t bus_n = pci_bus_num(vtd_as->bus);
uint16_t sid = PCI_BUILD_BDF(bus_n, vtd_as->devfn);
vtd_report_sid_ir_illegal_access(vtd_as->iommu_state, sid, vtd_as->pasid,
addr, is_write);
}
static MemTxResult vtd_mem_ir_read(void *opaque, hwaddr addr,
uint64_t *data, unsigned size,
MemTxAttrs attrs)
@ -4425,9 +4488,11 @@ static MemTxResult vtd_mem_ir_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size,
MemTxAttrs attrs)
{
IntelIOMMUState *s = opaque;
int ret = 0;
MSIMessage from = {}, to = {};
uint16_t sid = X86_IOMMU_SID_INVALID;
uint32_t pasid;
from.address = (uint64_t) addr + VTD_INTERRUPT_ADDR_FIRST;
from.data = (uint32_t) value;
@ -4435,9 +4500,16 @@ static MemTxResult vtd_mem_ir_write(void *opaque, hwaddr addr,
if (!attrs.unspecified) {
/* We have explicit Source ID */
sid = attrs.requester_id;
pasid = attrs.pid != 0 ? attrs.pid : PCI_NO_PASID;
if (attrs.address_type == PCI_AT_TRANSLATED &&
sid != X86_IOMMU_SID_INVALID) {
vtd_report_sid_ir_illegal_access(s, sid, pasid, from.address, true);
return MEMTX_ERROR;
}
}
ret = vtd_interrupt_remap_msi(opaque, &from, &to, sid, true);
ret = vtd_interrupt_remap_msi(s, &from, &to, sid, true);
if (ret) {
/* Drop this interrupt */
return MEMTX_ERROR;
@ -4462,30 +4534,6 @@ static const MemoryRegionOps vtd_mem_ir_ops = {
},
};
static void vtd_report_ir_illegal_access(VTDAddressSpace *vtd_as,
hwaddr addr, bool is_write)
{
IntelIOMMUState *s = vtd_as->iommu_state;
uint8_t bus_n = pci_bus_num(vtd_as->bus);
uint16_t sid = PCI_BUILD_BDF(bus_n, vtd_as->devfn);
bool is_fpd_set = false;
VTDContextEntry ce;
assert(vtd_as->pasid != PCI_NO_PASID);
/* Try out best to fetch FPD, we can't do anything more */
if (vtd_dev_to_context_entry(s, bus_n, vtd_as->devfn, &ce) == 0) {
is_fpd_set = ce.lo & VTD_CONTEXT_ENTRY_FPD;
if (!is_fpd_set && s->root_scalable) {
vtd_ce_get_pasid_fpd(s, &ce, &is_fpd_set, vtd_as->pasid);
}
}
vtd_report_fault(s, VTD_FR_SM_INTERRUPT_ADDR,
is_fpd_set, sid, addr, is_write,
true, vtd_as->pasid);
}
static MemTxResult vtd_mem_ir_fault_read(void *opaque, hwaddr addr,
uint64_t *data, unsigned size,
MemTxAttrs attrs)
@ -5145,14 +5193,10 @@ static IOMMUTLBEntry vtd_iommu_ats_do_translate(IOMMUMemoryRegion *iommu,
if (vtd_is_interrupt_addr(addr)) {
vtd_report_ir_illegal_access(vtd_as, addr, flags & IOMMU_WO);
vtd_prepare_error_entry(&entry);
entry.target_as = &address_space_memory;
entry.iova = 0;
entry.translated_addr = 0;
entry.addr_mask = ~VTD_PAGE_MASK_4K;
entry.perm = IOMMU_NONE;
entry.pasid = PCI_NO_PASID;
} else {
entry = vtd_iommu_translate(iommu, addr, flags, 0);
entry = vtd_iommu_translate(iommu, addr, flags, VTD_IDX_UNTRANSLATED);
}
return entry;

Loading…
Cancel
Save