diff mbox series

qemu: fix iotlb_to_section() for different AddressSpace

Message ID 20260511205209.511914-1-Quan.Sun@windriver.com
State New
Headers show
Series qemu: fix iotlb_to_section() for different AddressSpace | expand

Commit Message

Quan Sun May 11, 2026, 8:52 p.m. UTC
From: Quan Sun <Quan.Sun@windriver.com>

Backport upstream commit 854cd16e318e ("accel/tcg: Fix iotlb_to_section()
for different AddressSpace") to qemu 10.2.0.

The bug causes incorrect memory load/store when CPU access goes through
an IOMMUMemoryRegion that returns a different target AddressSpace, and the
fix replaces the section_index lookup with a direct MemoryRegionSection
pointer stored in CPUTLBEntryFull.

Note that the fix primarily targets ARM/RISC-V, but since it eliminates
iotlb_to_section() and performs the lookup based on CPUTLBEntryFull,
it may also help address some currently observed QEMU boot issues
on x86, e.g. the https://bugzilla.yoctoproject.org/show_bug.cgi?id=16259.

AI-Generated: kiro-cli

Signed-off-by: Quan Sun <Quan.Sun@windriver.com>
---
 meta/recipes-devtools/qemu/qemu.inc           |   1 +
 ...tlb_to_section-for-different-Address.patch | 274 ++++++++++++++++++
 2 files changed, 275 insertions(+)
 create mode 100644 meta/recipes-devtools/qemu/qemu/0001-accel-tcg-Fix-iotlb_to_section-for-different-Address.patch
diff mbox series

Patch

diff --git a/meta/recipes-devtools/qemu/qemu.inc b/meta/recipes-devtools/qemu/qemu.inc
index 7aa593bc5d..a8b4a1a541 100644
--- a/meta/recipes-devtools/qemu/qemu.inc
+++ b/meta/recipes-devtools/qemu/qemu.inc
@@ -33,6 +33,7 @@  SRC_URI = "https://download.qemu.org/${BPN}-${PV}.tar.xz \
            file://0010-configure-lookup-meson-exutable-from-PATH.patch \
            file://0011-qemu-Ensure-pip-and-the-python-venv-aren-t-used-for-.patch \
            file://0001-linux-user-elfload.c-Correction-to-HWCAP2-accessor.patch \
+           file://0001-accel-tcg-Fix-iotlb_to_section-for-different-Address.patch \
            file://qemu-guest-agent.init \
            file://qemu-guest-agent.udev \
            "
diff --git a/meta/recipes-devtools/qemu/qemu/0001-accel-tcg-Fix-iotlb_to_section-for-different-Address.patch b/meta/recipes-devtools/qemu/qemu/0001-accel-tcg-Fix-iotlb_to_section-for-different-Address.patch
new file mode 100644
index 0000000000..d19f872fc3
--- /dev/null
+++ b/meta/recipes-devtools/qemu/qemu/0001-accel-tcg-Fix-iotlb_to_section-for-different-Address.patch
@@ -0,0 +1,274 @@ 
+From 858e6bb252e075e09cca6e78299151d3af0bf5fb Mon Sep 17 00:00:00 2001
+From: Quan Sun <Quan.Sun@windriver.com>
+Date: Tue, 28 Apr 2026 14:56:36 -0400
+Subject: [PATCH] accel/tcg: Fix iotlb_to_section() for different AddressSpace
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+'CPUTLBEntryFull.xlat_section' stores section_index in last 12 bits to
+find the correct section when CPU access the IO region over the IOTLB.
+However, section_index is only unique inside single AddressSpace. If
+address space translation is over IOMMUMemoryRegion, it could return
+section from other AddressSpace. 'iotlb_to_section()' API only finds the
+sections from CPU's AddressSpace so that it couldn't find section in
+other AddressSpace. Thus, using 'iotlb_to_section()' API will find the
+wrong section and QEMU will have wrong load/store access.
+
+To fix this bug of iotlb_to_section(), store complete MemoryRegionSection
+pointer in CPUTLBEntryFull to replace the section_index in xlat_section.
+Rename 'xlat_section' to 'xlat_offset' as we remove last 12 bits
+section_index inside. Also, since we directly use section pointer in the
+CPUTLBEntryFull (full->section), we can remove the unused functions:
+iotlb_to_section(), memory_region_section_get_iotlb().
+
+This bug occurs only when
+(1) IOMMUMemoryRegion is in the path of CPU access.
+(2) IOMMUMemoryRegion returns different target_as and the section is in
+the IO region.
+
+This patch incorporates prerequisite changes from upstream commit
+94c6e9cf0440 ("accel/tcg: Send the CPUTLBEntryFull struct into
+io_prepare()") needed for the fix to apply cleanly.
+
+Upstream-Status: Backport [https://gitlab.com/qemu-project/qemu/-/commit/854cd16e318eed12de2995014b28d9f374c64bf7]
+
+Signed-off-by: Jim Shu <jim.shu@sifive.com>
+Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
+Tested-by: Mark Burton <mburton@qti.qualcomm.com>
+Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
+Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
+Signed-off-by: Quan Sun <Quan.Sun@windriver.com>
+---
+ accel/tcg/cputlb.c        | 32 +++++++++++++++-----------------
+ include/accel/tcg/iommu.h | 15 ---------------
+ include/exec/cputlb.h     |  4 ++--
+ include/hw/core/cpu.h     | 17 +++++++++--------
+ system/physmem.c          | 25 -------------------------
+ 5 files changed, 26 insertions(+), 67 deletions(-)
+
+diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
+index fd1606c85..fa0f4d8b3 100644
+--- a/accel/tcg/cputlb.c
++++ b/accel/tcg/cputlb.c
+@@ -1089,7 +1089,7 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx,
+         }
+     } else {
+         /* I/O or ROMD */
+-        iotlb = memory_region_section_get_iotlb(cpu, section) + xlat;
++        iotlb = xlat;
+         /*
+          * Writes to romd devices must go through MMIO to enable write.
+          * Reads to romd devices go through the ram_ptr found above,
+@@ -1140,10 +1140,9 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx,
+     /*
+      * When memory region is ram, iotlb contains a TARGET_PAGE_BITS
+      * aligned ram_addr_t of the page base of the target RAM.
+-     * Otherwise, iotlb contains
+-     *  - a physical section number in the lower TARGET_PAGE_BITS
+-     *  - the offset within section->mr of the page base (I/O, ROMD) with the
+-     *    TARGET_PAGE_BITS masked off.
++     * Otherwise, iotlb contains a TARGET_PAGE_BITS aligned
++     * offset within section->mr of the page base (I/O, ROMD)
++     *
+      * We subtract addr_page (which is page aligned and thus won't
+      * disturb the low bits) to give an offset which can be added to the
+      * (non-page-aligned) vaddr of the eventual memory access to get
+@@ -1153,7 +1152,8 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx,
+      */
+     desc->fulltlb[index] = *full;
+     full = &desc->fulltlb[index];
+-    full->xlat_section = iotlb - addr_page;
++    full->xlat_offset = iotlb - addr_page;
++    full->section = section;
+     full->phys_addr = paddr_page;
+
+     /* Now calculate the new entry */
+@@ -1269,14 +1269,14 @@ static inline void cpu_unaligned_access(CPUState *cpu, vaddr addr,
+ }
+
+ static MemoryRegionSection *
+-io_prepare(hwaddr *out_offset, CPUState *cpu, hwaddr xlat,
++io_prepare(hwaddr *out_offset, CPUState *cpu, CPUTLBEntryFull *full,
+            MemTxAttrs attrs, vaddr addr, uintptr_t retaddr)
+ {
+     MemoryRegionSection *section;
+     hwaddr mr_offset;
+
+-    section = iotlb_to_section(cpu, xlat, attrs);
+-    mr_offset = (xlat & TARGET_PAGE_MASK) + addr;
++    section = full->section;
++    mr_offset = full->xlat_offset + addr;
+     cpu->mem_io_pc = retaddr;
+     if (!cpu->neg.can_do_io) {
+         cpu_io_recompile(cpu, retaddr);
+@@ -1335,7 +1335,7 @@ static bool victim_tlb_hit(CPUState *cpu, size_t mmu_idx, size_t index,
+ static void notdirty_write(CPUState *cpu, vaddr mem_vaddr, unsigned size,
+                            CPUTLBEntryFull *full, uintptr_t retaddr)
+ {
+-    ram_addr_t ram_addr = mem_vaddr + full->xlat_section;
++    ram_addr_t ram_addr = mem_vaddr + full->xlat_offset;
+
+     trace_memory_notdirty_write_access(mem_vaddr, ram_addr, size);
+
+@@ -1592,9 +1592,7 @@ bool tlb_plugin_lookup(CPUState *cpu, vaddr addr, int mmu_idx,
+
+     /* We must have an iotlb entry for MMIO */
+     if (tlb_addr & TLB_MMIO) {
+-        MemoryRegionSection *section =
+-            iotlb_to_section(cpu, full->xlat_section & ~TARGET_PAGE_MASK,
+-                             full->attrs);
++        MemoryRegionSection *section = full->section;
+         data->is_io = true;
+         data->mr = section->mr;
+     } else {
+@@ -1980,7 +1978,7 @@ static uint64_t do_ld_mmio_beN(CPUState *cpu, CPUTLBEntryFull *full,
+     tcg_debug_assert(size > 0 && size <= 8);
+
+     attrs = full->attrs;
+-    section = io_prepare(&mr_offset, cpu, full->xlat_section, attrs, addr, ra);
++    section = io_prepare(&mr_offset, cpu, full, attrs, addr, ra);
+     mr = section->mr;
+
+     BQL_LOCK_GUARD();
+@@ -2001,7 +1999,7 @@ static Int128 do_ld16_mmio_beN(CPUState *cpu, CPUTLBEntryFull *full,
+     tcg_debug_assert(size > 8 && size <= 16);
+
+     attrs = full->attrs;
+-    section = io_prepare(&mr_offset, cpu, full->xlat_section, attrs, addr, ra);
++    section = io_prepare(&mr_offset, cpu, full, attrs, addr, ra);
+     mr = section->mr;
+
+     BQL_LOCK_GUARD();
+@@ -2521,7 +2519,7 @@ static uint64_t do_st_mmio_leN(CPUState *cpu, CPUTLBEntryFull *full,
+     tcg_debug_assert(size > 0 && size <= 8);
+
+     attrs = full->attrs;
+-    section = io_prepare(&mr_offset, cpu, full->xlat_section, attrs, addr, ra);
++    section = io_prepare(&mr_offset, cpu, full, attrs, addr, ra);
+     mr = section->mr;
+
+     BQL_LOCK_GUARD();
+@@ -2541,7 +2539,7 @@ static uint64_t do_st16_mmio_leN(CPUState *cpu, CPUTLBEntryFull *full,
+     tcg_debug_assert(size > 8 && size <= 16);
+
+     attrs = full->attrs;
+-    section = io_prepare(&mr_offset, cpu, full->xlat_section, attrs, addr, ra);
++    section = io_prepare(&mr_offset, cpu, full, attrs, addr, ra);
+     mr = section->mr;
+
+     BQL_LOCK_GUARD();
+diff --git a/include/accel/tcg/iommu.h b/include/accel/tcg/iommu.h
+index 90cfd6c0e..547f8ea0e 100644
+--- a/include/accel/tcg/iommu.h
++++ b/include/accel/tcg/iommu.h
+@@ -14,18 +14,6 @@
+ #include "exec/hwaddr.h"
+ #include "exec/memattrs.h"
+
+-/**
+- * iotlb_to_section:
+- * @cpu: CPU performing the access
+- * @index: TCG CPU IOTLB entry
+- *
+- * Given a TCG CPU IOTLB entry, return the MemoryRegionSection that
+- * it refers to. @index will have been initially created and returned
+- * by memory_region_section_get_iotlb().
+- */
+-MemoryRegionSection *iotlb_to_section(CPUState *cpu,
+-                                      hwaddr index, MemTxAttrs attrs);
+-
+ MemoryRegionSection *address_space_translate_for_iotlb(CPUState *cpu,
+                                                        int asidx,
+                                                        hwaddr addr,
+@@ -34,8 +22,5 @@ MemoryRegionSection *address_space_translate_for_iotlb(CPUState *cpu,
+                                                        MemTxAttrs attrs,
+                                                        int *prot);
+
+-hwaddr memory_region_section_get_iotlb(CPUState *cpu,
+-                                       MemoryRegionSection *section);
+-
+ #endif
+
+diff --git a/include/exec/cputlb.h b/include/exec/cputlb.h
+index 9bec0e789..16f866990 100644
+--- a/include/exec/cputlb.h
++++ b/include/exec/cputlb.h
+@@ -43,8 +43,8 @@ void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length);
+  * @full: the details of the tlb entry
+  *
+  * Add an entry to @cpu tlb index @mmu_idx.  All of the fields of
+- * @full must be filled, except for xlat_section, and constitute
+- * the complete description of the translated page.
++ * @full must be filled, except for xlat_offset & section, and
++ * constitute the complete description of the translated page.
+  *
+  * This is generally called by the target tlb_fill function after
+  * having performed a successful page table walk to find the physical
+diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h
+index 961505177..a3db3f66f 100644
+--- a/include/hw/core/cpu.h
++++ b/include/hw/core/cpu.h
+@@ -214,15 +214,16 @@ typedef uint32_t MMUIdxMap;
+  */
+ struct CPUTLBEntryFull {
+     /*
+-     * @xlat_section contains:
+-     *  - in the lower TARGET_PAGE_BITS, a physical section number
+-     *  - with the lower TARGET_PAGE_BITS masked off, an offset which
+-     *    must be added to the virtual address to obtain:
+-     *     + the ram_addr_t of the target RAM (if the physical section
+-     *       number is PHYS_SECTION_NOTDIRTY or PHYS_SECTION_ROM)
+-     *     + the offset within the target MemoryRegion (otherwise)
++     * @xlat_offset: TARGET_PAGE_BITS aligned offset which must be added to
++     * the virtual address to obtain:
++     *   + the ram_addr_t of the target RAM (if the physical section
++     *     number is PHYS_SECTION_NOTDIRTY or PHYS_SECTION_ROM)
++     *   + the offset within the target MemoryRegion (otherwise)
+      */
+-    hwaddr xlat_section;
++    hwaddr xlat_offset;
++
++    /* @section contains physical section. */
++    MemoryRegionSection *section;
+
+     /*
+      * @phys_addr contains the physical address in the address space
+diff --git a/system/physmem.c b/system/physmem.c
+index c9869e404..a21e7ca64 100644
+--- a/system/physmem.c
++++ b/system/physmem.c
+@@ -748,31 +748,6 @@ translate_fail:
+     return &d->map.sections[PHYS_SECTION_UNASSIGNED];
+ }
+
+-MemoryRegionSection *iotlb_to_section(CPUState *cpu,
+-                                      hwaddr index, MemTxAttrs attrs)
+-{
+-    int asidx = cpu_asidx_from_attrs(cpu, attrs);
+-    CPUAddressSpace *cpuas = &cpu->cpu_ases[asidx];
+-    AddressSpaceDispatch *d = address_space_to_dispatch(cpuas->as);
+-    int section_index = index & ~TARGET_PAGE_MASK;
+-    MemoryRegionSection *ret;
+-
+-    assert(section_index < d->map.sections_nb);
+-    ret = d->map.sections + section_index;
+-    assert(ret->mr);
+-    assert(ret->mr->ops);
+-
+-    return ret;
+-}
+-
+-/* Called from RCU critical section */
+-hwaddr memory_region_section_get_iotlb(CPUState *cpu,
+-                                       MemoryRegionSection *section)
+-{
+-    AddressSpaceDispatch *d = flatview_to_dispatch(section->fv);
+-    return section - d->map.sections;
+-}
+-
+ #endif /* CONFIG_TCG */
+
+ void cpu_address_space_init(CPUState *cpu, int asidx,
+--
+2.43.0