@samitouri / QOSamiQemu / commits / 1ae8e5b0f1

system/physmem: Use translate_for_debug() in cpu_memory_rw_debug()

Currently cpu_memory_rw_debug() assumes page-granularity for translations, and it works in a loop where each iteration translates for the vaddr rounded down to a page boundary and then copies up to the end of the page boundary. Rewrite it to use the new cpu_translate_for_debug(): we no longer want to round down the input address, and the boundary we copy up to is now determined by the lg_page_size it returns rather than being assumed to be page-sized. This, together with the implementation of translate_for_debug for Arm targets, fixes the bug where semihosting would incorrectly fail to access parameter blocks that were in memory where the start of the 4K region they were in was inaccessible due to MPU region settings, even if the parameter block itself was readable. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3292 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260417173105.1648172-18-peter.maydell@linaro.org Acked-by: Peter Xu <peterx@redhat.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-ID: <20260430093810.2762539-19-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>

Peter Maydell committed Apr 30, 2026 at 10:38 UTC 1ae8e5b0f1a40f4303cd145b998dc6e8d75909f1
1 file changed +24 -14
system/physmem.c
+24 -14
@@ -4030,28 +4030,38 @@ address_space_write_cached_slow(MemoryRegionCache *cache, hwaddr addr,
4030 int cpu_memory_rw_debug(CPUState *cpu, vaddr addr,
4031 void *ptr, size_t len, bool is_write)
4032 {
4033 - hwaddr phys_addr;
4034 - vaddr l, page;
4033 uint8_t *buf = ptr;
4034
4035 cpu_synchronize_state(cpu);
4036 while (len > 0) {
4037 int asidx;
4040 - MemTxAttrs attrs;
4038 + TranslateForDebugResult tres;
4039 MemTxResult res;
4040 + hwaddr blk_base, blk_size, l;
4041
4043 - page = addr & TARGET_PAGE_MASK;
4044 - phys_addr = cpu_get_phys_addr_attrs_debug(cpu, page, &attrs);
4045 - asidx = cpu_asidx_from_attrs(cpu, attrs);
4046 - /* if no physical page mapped, return an error */
4047 - if (phys_addr == -1)
4042 + if (!cpu_translate_for_debug(cpu, addr, &tres)) {
4043 + /* Return error if no physical page mapped */
4044 return -1;
4049 - l = (page + TARGET_PAGE_SIZE) - addr;
4050 - if (l > len)
4051 - l = len;
4052 - phys_addr += (addr & ~TARGET_PAGE_MASK);
4053 - res = address_space_rw(cpu->cpu_ases[asidx].as, phys_addr, attrs, buf,
4054 - l, is_write);
4045 + }
4046 + asidx = cpu_asidx_from_attrs(cpu, tres.attrs);
4047 + /*
4048 + * Clamp the amount we read to not go beyond a page even if
4049 + * the CPU returned a larger lg_page_size, in case this access
4050 + * is to a memory-mapped IO region.
4051 + */
4052 + tres.lg_page_size = MIN(tres.lg_page_size, TARGET_PAGE_BITS);
4053 + /*
4054 + * Find the length in bytes from tres.physaddr to the end of the
4055 + * block whose size is 1 << tres.lg_page_size; we will access
4056 + * that much in one go.
4057 + */
4058 + blk_size = 1ULL << tres.lg_page_size;
4059 + blk_base = ROUND_DOWN(tres.physaddr, blk_size);
4060 + l = blk_base + blk_size - tres.physaddr;
4061 + l = MIN(l, len);
4062 +
4063 + res = address_space_rw(cpu->cpu_ases[asidx].as, tres.physaddr,
4064 + tres.attrs, buf, l, is_write);
4065 if (res != MEMTX_OK) {
4066 return -1;
4067 }