exec: abstract address_space_do_translate()

This function is an abstraction helper for address_space_translate() and
address_space_get_iotlb_entry(). It does the lookup of address into
memory region section, then does proper IOMMU translation if necessary.
Refactor the two existing functions to use it.

This fixes vhost when IOMMU is disabled by guest.

Tested-by: Maxime Coquelin <maxime.coquelin@redhat.com>
Signed-off-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
Peter Xu 2017-05-17 16:57:42 +08:00 committed by Michael S. Tsirkin
parent 3936161f1f
commit a764040cc8
1 changed files with 75 additions and 40 deletions

115
exec.c
View File

@ -463,42 +463,12 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x
}
/* Called from RCU critical section */
IOMMUTLBEntry address_space_get_iotlb_entry(AddressSpace *as, hwaddr addr,
bool is_write)
{
IOMMUTLBEntry iotlb = {0};
MemoryRegionSection *section;
MemoryRegion *mr;
for (;;) {
AddressSpaceDispatch *d = atomic_rcu_read(&as->dispatch);
section = address_space_lookup_region(d, addr, false);
addr = addr - section->offset_within_address_space
+ section->offset_within_region;
mr = section->mr;
if (!mr->iommu_ops) {
break;
}
iotlb = mr->iommu_ops->translate(mr, addr, is_write);
if (!(iotlb.perm & (1 << is_write))) {
iotlb.target_as = NULL;
break;
}
addr = ((iotlb.translated_addr & ~iotlb.addr_mask)
| (addr & iotlb.addr_mask));
as = iotlb.target_as;
}
return iotlb;
}
/* Called from RCU critical section */
MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr,
hwaddr *xlat, hwaddr *plen,
bool is_write)
static MemoryRegionSection address_space_do_translate(AddressSpace *as,
hwaddr addr,
hwaddr *xlat,
hwaddr *plen,
bool is_write,
bool is_mmio)
{
IOMMUTLBEntry iotlb;
MemoryRegionSection *section;
@ -506,7 +476,7 @@ MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr,
for (;;) {
AddressSpaceDispatch *d = atomic_rcu_read(&as->dispatch);
section = address_space_translate_internal(d, addr, &addr, plen, true);
section = address_space_translate_internal(d, addr, &addr, plen, is_mmio);
mr = section->mr;
if (!mr->iommu_ops) {
@ -518,19 +488,84 @@ MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr,
| (addr & iotlb.addr_mask));
*plen = MIN(*plen, (addr | iotlb.addr_mask) - addr + 1);
if (!(iotlb.perm & (1 << is_write))) {
mr = &io_mem_unassigned;
break;
goto translate_fail;
}
as = iotlb.target_as;
}
*xlat = addr;
return *section;
translate_fail:
return (MemoryRegionSection) { .mr = &io_mem_unassigned };
}
/* Called from RCU critical section */
IOMMUTLBEntry address_space_get_iotlb_entry(AddressSpace *as, hwaddr addr,
bool is_write)
{
MemoryRegionSection section;
hwaddr xlat, plen;
/* Try to get maximum page mask during translation. */
plen = (hwaddr)-1;
/* This can never be MMIO. */
section = address_space_do_translate(as, addr, &xlat, &plen,
is_write, false);
/* Illegal translation */
if (section.mr == &io_mem_unassigned) {
goto iotlb_fail;
}
/* Convert memory region offset into address space offset */
xlat += section.offset_within_address_space -
section.offset_within_region;
if (plen == (hwaddr)-1) {
/*
* We use default page size here. Logically it only happens
* for identity mappings.
*/
plen = TARGET_PAGE_SIZE;
}
/* Convert to address mask */
plen -= 1;
return (IOMMUTLBEntry) {
.target_as = section.address_space,
.iova = addr & ~plen,
.translated_addr = xlat & ~plen,
.addr_mask = plen,
/* IOTLBs are for DMAs, and DMA only allows on RAMs. */
.perm = IOMMU_RW,
};
iotlb_fail:
return (IOMMUTLBEntry) {0};
}
/* Called from RCU critical section */
MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr,
hwaddr *xlat, hwaddr *plen,
bool is_write)
{
MemoryRegion *mr;
MemoryRegionSection section;
/* This can be MMIO, so setup MMIO bit. */
section = address_space_do_translate(as, addr, xlat, plen, is_write, true);
mr = section.mr;
if (xen_enabled() && memory_access_is_direct(mr, is_write)) {
hwaddr page = ((addr & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE) - addr;
*plen = MIN(page, *plen);
}
*xlat = addr;
return mr;
}