Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion hw/arm/dart.c
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,13 @@ static void apple_dart_notify_remap(AppleDARTMapperInstance* mapper, uint32_t in
event.entry.addr_mask = HWADDR_MAX;
memory_region_notify_iommu(iommu, 0, event);

IOMMU_NOTIFIER_FOREACH (notifier, iommu) { memory_region_iommu_replay(iommu, notifier); }
// A replay walks the whole page table, and only a notifier that asked for mappings gets anything
// out of it. TCG registers one notifier per CPU that has translated through this stream, for unmaps
// alone, and drops every mapping it is handed — so for those the walk was pure cost, paid again on
// each TLB operation the guest issues.
IOMMU_NOTIFIER_FOREACH (notifier, iommu) {
if ((notifier->notifier_flags & IOMMU_NOTIFIER_MAP) != 0) { memory_region_iommu_replay(iommu, notifier); }
}
}

static void apple_dart_mapper_reg_write(void* opaque, hwaddr addr, uint64_t data, unsigned size)
Expand Down
19 changes: 18 additions & 1 deletion system/physmem.c
Original file line number Diff line number Diff line change
Expand Up @@ -570,12 +570,29 @@ typedef struct TCGIOMMUNotifier
bool active;
} TCGIOMMUNotifier;

static void tcg_iommu_flush_cpu(CPUState* cpu, run_on_cpu_data data) { tlb_flush(cpu); }

static void tcg_iommu_unmap_notify(IOMMUNotifier* n, IOMMUTLBEntry* iotlb)
{
TCGIOMMUNotifier* notifier = container_of(n, TCGIOMMUNotifier, n);

if (!notifier->active) { return; }
tlb_flush(notifier->cpu);
/*
* The IOMMU changes on whichever thread wrote to it, which is seldom the thread of the CPU
* whose TLB this is: on the t8030 a DART mapping revoked by an application core is one the
* SEP core, or another application core, has translated through. tlb_flush works on the
* calling thread's TLB — it frees and reallocates the tables it resizes — while their owner
* goes on reading them without a lock. Done from here, the owner read freed memory as TLB
* entries and wrote guest data wherever those pointed: a heap that malloc then spun in
* forever, dirty bitmaps with ASCII for pointers, "Bad ram pointer". A flush for another CPU
* is queued to that CPU instead, the way tcg_commit hands over a changed memory map.
*/
if (notifier->cpu->halt_cond && !qemu_cpu_is_self(notifier->cpu)) {
async_run_on_cpu(notifier->cpu, tcg_iommu_flush_cpu, RUN_ON_CPU_NULL);
}
else {
tlb_flush(notifier->cpu);
}
notifier->active = false;
/* We leave the notifier struct on the list to avoid reallocating it later.
* Generally the number of IOMMUs a CPU deals with will be small.
Expand Down
Loading