:p
atchew
Login
Hi; here's a pullreq for arm; these are all bug fixes. thanks -- PMM The following changes since commit bd6079a7a1d14de0918a7715ef6db85dc32de3bb: Merge tag 'next-pull-request' of https://gitlab.com/peterx/qemu into staging (2026-07-20 11:12:38 -0400) are available in the Git repository at: https://gitlab.com/pm215/qemu.git tags/pull-target-arm-20260721 for you to fetch changes up to b693da615a7ab2adae7383fecb87b0e13966256e: Revert "target/arm: Build cpu32-system.o as common object" (2026-07-20 19:05:30 +0100) ---------------------------------------------------------------- target-arm queue: * hw/char: imx_serial: add missing migration state * hw/net/xilinx_axienet: Fix PHY register 17 link status reporting * target/arm: GICv5 cpuif: gicr_cdia_read() ppibit should be 64 bits * target/arm: GICv5 cpuif: Writes to ICC_APR_EL1 can change IRQ/FIQ * target/ppc: Remove unused helper_lscbx() * target/arm: Fix testing of raw mtx value * hw/dma/omap_dma: don't allow guest to memcopy arbitrary memory * target/arm: restore missing A-profile CPU types to qemu-arm ---------------------------------------------------------------- Jay Chang (1): hw/net/xilinx_axienet: Fix PHY register 17 link status reporting Peter Maydell (13): target/arm: GICv5 cpuif: gicr_cdia_read() ppibit should be 64 bits target/arm: GICv5 cpuif: Writes to ICC_APR_EL1 can change IRQ/FIQ target/ppc: Remove unused helper_lscbx() hw/dma/soc_dma: Remove soc_dma_port_fifo support hw/dma/soc_dma: Simplify soc_dma_ch_update() hw/dma/soc_dma: Remove union from memmap_entry_s struct hw/dma/omap_dma: Be more careful about overflow in transfer setup hw/dma/soc_dma: dma bytes is uint64_t hw/dma/soc_dma: Use physical_memory_map() for mem2mem transfers hw/dma/soc_dma: Remove unused mem.base, paddr fields include/hw/arm/omap_dma.h: Move to include/hw/dma MAINTAINERS: Add soc_dma to OMAP section Revert "target/arm: Build cpu32-system.o as common object" Richard Henderson (1): target/arm: Fix testing of raw mtx value Tao Ding (1): hw/char: imx_serial: add missing migration state MAINTAINERS | 2 + hw/arm/omap1.c | 8 +- hw/char/imx_serial.c | 5 +- hw/dma/omap_dma.c | 37 +++--- hw/dma/soc_dma.c | 196 ++++++++++------------------- hw/net/xilinx_axienet.c | 3 + include/hw/{arm => dma}/soc_dma.h | 33 ++--- target/arm/tcg/{cpu32-system.c => cpu32.c} | 10 +- target/arm/tcg/gicv5-cpuif.c | 4 +- target/arm/tcg/meson.build | 5 +- target/arm/tcg/mte_helper.c | 25 ++-- target/ppc/helper.h | 1 - target/ppc/mem_helper.c | 28 ----- 13 files changed, 136 insertions(+), 221 deletions(-) rename include/hw/{arm => dma}/soc_dma.h (70%) rename target/arm/tcg/{cpu32-system.c => cpu32.c} (99%)
From: Tao Ding <dingtao0430@163.com> The imx_serial vmstate is missing the ucr2 field. This register includes important state like the transmit enable and receive enable bits, so it's likely that after a migration the UART will be in a completely broken state. This bug has been present ever since the UART code was first added to QEMU. Add ucr2 from imx_serial to vmstate, and increment the version_id. This is a migration compatibility break, but this UART is only used in the various imx-based boards, where we are OK with compat breaks. Migrating on sabrelite can reproduce this issue: 1. Prepare the U-Boot required for sabrelite. (according to sabrelite.rst) 2. Compile qemu $ mkdir build && cd build && ../configure --target-list="arm-softmmu" && make -j4 3. Start sabrelite and prepare for migration $ ./build/qemu-system-arm -M sabrelite \ -smp 1 -m 1G -display none -serial null -serial mon:stdio \ -kernel ~/u-boot 4. Enter qemu monitor after uboot. (ctrl + a + c) (qemu) stop (qemu) xp /4wx 0x021e8084 021e8084: 0x00004027 0x00000784 0x00008000 0x00000a01 (qemu) migrate -d file:vmstate (qemu) q Load the migrated vmstate, before repairing: $ ./build/qemu-system-arm -M sabrelite \ -smp 1 -m 1G -display none -serial null -serial mon:stdio \ -kernel ~/u-boot -incoming file:vmstate (ctrl + a + c) QEMU 11.0.50 monitor - type 'help' for more information (qemu) xp /4wx 0x021e8084 021e8084: 0x00000004 0x00000784 0x00008000 0x00000a01 (qemu) q It can be found that the data for address 0x021e8084 (register of usr2 in imx_serial of sabrelite) is not the data before the migration. After being repaired: $ ./build/qemu-system-arm -M sabrelite \ -smp 1 -m 1G -display none -serial null -serial mon:stdio \ -kernel ~/u-boot -incoming file:vmstate (ctrl + a + c) QEMU 11.0.50 monitor - type 'help' for more information (qemu) xp /4wx 0x021e8084 021e8084: 0x00004027 0x00000784 0x00008000 0x00000a01 Cc: qemu-stable@nongnu.org Fixes: 40b6f91151 ("i.MX: UART support") Signed-off-by: Tao Ding <dingtao0430@163.com> Message-id: 20260715131819.14827-2-dingtao0430@163.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/char/imx_serial.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c index XXXXXXX..XXXXXXX 100644 --- a/hw/char/imx_serial.c +++ b/hw/char/imx_serial.c @@ -XXX,XX +XXX,XX @@ static const VMStateDescription vmstate_imx_serial = { .name = TYPE_IMX_SERIAL, - .version_id = 3, - .minimum_version_id = 3, + .version_id = 4, + .minimum_version_id = 4, .fields = (const VMStateField[]) { VMSTATE_FIFO32(rx_fifo, IMXSerialState), VMSTATE_TIMER(ageing_timer, IMXSerialState), VMSTATE_UINT32(usr1, IMXSerialState), VMSTATE_UINT32(usr2, IMXSerialState), VMSTATE_UINT32(ucr1, IMXSerialState), + VMSTATE_UINT32(ucr2, IMXSerialState), VMSTATE_UINT32(uts1, IMXSerialState), VMSTATE_UINT32(onems, IMXSerialState), VMSTATE_UINT32(ufcr, IMXSerialState), -- 2.43.0
From: Jay Chang <jay.chang@sifive.com> The Marvell 88E1111 PHY register 17 (PHY Specific Status Register) bit 10 reports real-time link status. Previously, this register returned a fixed value of 0x8800 with bit 10 always cleared, causing U-Boot to always detect "No link" even when the link was up. Signed-off-by: Jay Chang <jay.chang@sifive.com> Reviewed-by: Frank Chang <frank.chang@sifive.com> Message-id: 20260713064617.85374-1-jay.chang@sifive.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/net/xilinx_axienet.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c index XXXXXXX..XXXXXXX 100644 --- a/hw/net/xilinx_axienet.c +++ b/hw/net/xilinx_axienet.c @@ -XXX,XX +XXX,XX @@ static unsigned int tdk_read(struct PHY *phy, unsigned int req) case 17: /* Marvell PHY on many xilinx boards. */ r = 0x8000; /* 1000Mb */ + if (phy->link) { + r |= 0x0400; /* Link is up */ + } break; case 18: { -- 2.43.0
In gicr_cdia_read() we turn a PPI interrupt ID into a register index and a bit mask with a 1 for the bit we want to change: ppireg = id / 64; ppibit = 1ULL << (id % 64); However, we used the wrong type for ppibit, making it a uint32_t. If 'id' is too large we'll shift off the end, so we won't ever update the state of PPIs with indexes above 31. This didn't have any visible effects because the currently allocated architected PPIs are indexes 0..31, so you'd only see this if for some reason a guest was manually marking as pending a PPI in 32..63. Fix the type of ppibit to the intended 64 bit width. Fixes: 3f79212abae89 ("target/arm: GICv5 cpuif: Implement GICR CDIA command") Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260714091806.3568281-2-peter.maydell@linaro.org --- target/arm/tcg/gicv5-cpuif.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/arm/tcg/gicv5-cpuif.c b/target/arm/tcg/gicv5-cpuif.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/gicv5-cpuif.c +++ b/target/arm/tcg/gicv5-cpuif.c @@ -XXX,XX +XXX,XX @@ static uint64_t gicr_cdia_read(CPUARMState *env, const ARMCPRegInfo *ri) switch (type) { case GICV5_PPI: { - uint32_t ppireg, ppibit; + uint32_t ppireg; + uint64_t ppibit; assert(id < GICV5_NUM_PPIS); ppireg = id / 64; -- 2.43.0
The ICC_APR_EL1 register values change the current running priority, which we use in our calculations in gic_hppi(). Changing them can affect whether we have a HPPI, and so the value of the IRQ/FIQ outputs. We need to trigger an update in the register writefn. Fixes: 9bd90bddb79 ("target/arm: GICv5 cpuif: Signal IRQ or FIQ") Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260714091806.3568281-3-peter.maydell@linaro.org --- target/arm/tcg/gicv5-cpuif.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/arm/tcg/gicv5-cpuif.c b/target/arm/tcg/gicv5-cpuif.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/gicv5-cpuif.c +++ b/target/arm/tcg/gicv5-cpuif.c @@ -XXX,XX +XXX,XX @@ static void gic_icc_apr_el1_write(CPUARMState *env, const ARMCPRegInfo *ri, GICv5Domain domain = gicv5_logical_domain(env); value &= 0xffffffff; env->gicv5_cpuif.icc_apr[domain] = value; + gicv5_update_irq_fiq(env); } static uint64_t gic_icc_apr_el1_read(CPUARMState *env, const ARMCPRegInfo *ri) -- 2.43.0
The helper_lscbx() function isn't called anywhere. It used to be used by the PPC 601 CPU support; we removed that in commit 005b69fdcc in 2022 but missed this helper function. Fixes: 005b69fdcc ("target/ppc: Remove PowerPC 601 CPUs") Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Amit Machhiwal <amachhiw@linux.ibm.com> Message-id: 20260709095056.1803725-1-peter.maydell@linaro.org --- target/ppc/helper.h | 1 - target/ppc/mem_helper.c | 28 ---------------------------- 2 files changed, 29 deletions(-) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index XXXXXXX..XXXXXXX 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -XXX,XX +XXX,XX @@ DEF_HELPER_FLAGS_2(dcbzl, TCG_CALL_NO_WG, void, env, tl) #endif DEF_HELPER_FLAGS_2(icbi, TCG_CALL_NO_WG, void, env, tl) DEF_HELPER_FLAGS_2(icbiep, TCG_CALL_NO_WG, void, env, tl) -DEF_HELPER_5(lscbx, tl, env, tl, i32, i32, i32) #if defined(TARGET_PPC64) DEF_HELPER_4(DIVDEU, i64, env, i64, i64, i32) diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -XXX,XX +XXX,XX @@ void helper_icbiep(CPUPPCState *env, target_ulong addr) #endif } -/* XXX: to be tested */ -target_ulong helper_lscbx(CPUPPCState *env, target_ulong addr, uint32_t reg, - uint32_t ra, uint32_t rb) -{ - int i, c, d; - - d = 24; - for (i = 0; i < xer_bc; i++) { - c = cpu_ldub_data_ra(env, addr, GETPC()); - addr = addr_add(env, addr, 1); - /* ra (if not 0) and rb are never modified */ - if (likely(reg != rb && (ra == 0 || reg != ra))) { - env->gpr[reg] = (env->gpr[reg] & ~(0xFF << d)) | (c << d); - } - if (unlikely(c == xer_cmp)) { - break; - } - if (likely(d != 0)) { - d -= 8; - } else { - d = 24; - reg++; - reg = reg & 0x1F; - } - } - return i; -} - /*****************************************************************************/ /* Altivec extension helpers */ #if HOST_BIG_ENDIAN -- 2.43.0
From: Richard Henderson <richard.henderson@linaro.org> MTX is always a pair of bits, one for each half of the address space. Testing it like a boolean is incorrect. Introduce raw_mte_check, a mirror of the similar mte_check function that applies when MTX is passed in MTEDESC. Fixes: 8912ceced815 ("target/arm: load on canonical tag loads ext bits") Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260717161430.37264-1-richard.henderson@linaro.org Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/mte_helper.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -XXX,XX +XXX,XX @@ int load_tag1(uint64_t ptr, uint8_t *mem) return extract32(*mem, ofs, 4); } +/* Like mtx_check, but simple mtx bit pair instead of MTEDESC. */ +static bool raw_mtx_check(unsigned mtx, unsigned bit55) +{ + return (mtx >> bit55) & 1; +} + uint64_t HELPER(ldg)(CPUARMState *env, uint64_t ptr, uint64_t xt, uint32_t mtx) { int mmu_idx = arm_env_mmu_index(env); @@ -XXX,XX +XXX,XX @@ uint64_t HELPER(ldg)(CPUARMState *env, uint64_t ptr, uint64_t xt, uint32_t mtx) /* Load if page supports tags. */ if (mem) { rtag = load_tag1(ptr, mem); - } else if (mtx) { - uint64_t bit55 = extract64(ptr, 55, 1); - rtag = 0xF * bit55; + } else { + bool bit55 = extract64(ptr, 55, 1); + if (raw_mtx_check(mtx, bit55)) { + rtag = 0xF * bit55; + } } return address_with_allocation_tag(xt, rtag); @@ -XXX,XX +XXX,XX @@ static inline void do_stg(CPUARMState *env, uint64_t ptr, uint64_t xt, /* Store if page supports tags. */ if (mem) { store1(ptr, mem, allocation_tag_from_addr(xt)); - } else if (mtx) { + } else if (raw_mtx_check(mtx, extract64(ptr, 55, 1))) { canonical_tag_write_fail(env, ptr, ra); } } @@ -XXX,XX +XXX,XX @@ static inline void do_st2g(CPUARMState *env, uint64_t ptr, uint64_t xt, uint8_t *mem1, *mem2; check_tag_aligned(env, ptr, ra); + mtx = raw_mtx_check(mtx, extract64(ptr, 55, 1)); /* * Trap if accessing an invalid page(s). @@ -XXX,XX +XXX,XX @@ uint64_t HELPER(ldgm)(CPUARMState *env, uint64_t ptr, uint32_t mtx) /* The tag is squashed to zero if the page does not support tags. */ if (!tag_mem) { /* Load canonical value if mtx is set (untagged memory region) */ - if (mtx) { - bool bit55 = extract64(ptr, 55, 1); + bool bit55 = extract64(ptr, 55, 1); + if (raw_mtx_check(mtx, bit55)) { ret = extract64(-bit55, 0, 1 << gm_bs); shift = extract64(ptr, LOG2_TAG_GRANULE, 4) * 4; return ret << shift; @@ -XXX,XX +XXX,XX @@ void HELPER(stgm)(CPUARMState *env, uint64_t ptr, uint64_t val, uint32_t mtx) */ if (!tag_mem) { /* Storing tags to canonically tagged region: fault. */ - if (mtx) { + if (raw_mtx_check(mtx, extract64(ptr, 55, 1))) { canonical_tag_write_fail(env, ptr, ra); } return; @@ -XXX,XX +XXX,XX @@ void HELPER(stzgm_tags)(CPUARMState *env, uint64_t ptr, uint64_t val, if (mem) { int tag_pair = (val & 0xf) * 0x11; memset(mem, tag_pair, tag_bytes); - } else if (mtx) { + } else if (raw_mtx_check(mtx, extract64(ptr, 55, 1))) { canonical_tag_write_fail(env, ptr, ra); } } -- 2.43.0
Our current single OMAP SoC doesn't call the soc_dma_port_add_fifo(), soc_dma_port_add_fifo_in() or soc_dma_port_add_fifo_out() functions. Remove them, plus the soc_dma_port_fifo handling that only those functions needed. The motivation for this is that it removes a lot of code that is careless about the fact that the largest possible DMA transfer is more bits than will fit into an "int" variable, and which does direct accesses to host memory pointers into guest backing RAM without doing bounds checks. Deleting this code means we don't have to audit and update it. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Message-id: 20260710105907.2570621-2-peter.maydell@linaro.org --- hw/dma/soc_dma.c | 105 +-------------------------------------- include/hw/arm/soc_dma.h | 24 ++------- 2 files changed, 5 insertions(+), 124 deletions(-) diff --git a/hw/dma/soc_dma.c b/hw/dma/soc_dma.c index XXXXXXX..XXXXXXX 100644 --- a/hw/dma/soc_dma.c +++ b/hw/dma/soc_dma.c @@ -XXX,XX +XXX,XX @@ static void transfer_mem2mem(struct soc_dma_ch_s *ch) ch->paddr[1] += ch->bytes; } -static void transfer_mem2fifo(struct soc_dma_ch_s *ch) -{ - ch->io_fn[1](ch->io_opaque[1], ch->paddr[0], ch->bytes); - ch->paddr[0] += ch->bytes; -} - -static void transfer_fifo2mem(struct soc_dma_ch_s *ch) -{ - ch->io_fn[0](ch->io_opaque[0], ch->paddr[1], ch->bytes); - ch->paddr[1] += ch->bytes; -} - -/* This is further optimisable but isn't very important because often - * DMA peripherals forbid this kind of transfers and even when they don't, - * oprating systems may not need to use them. */ -static void *fifo_buf; -static int fifo_size; -static void transfer_fifo2fifo(struct soc_dma_ch_s *ch) -{ - if (ch->bytes > fifo_size) - fifo_buf = g_realloc(fifo_buf, fifo_size = ch->bytes); - - /* Implement as transfer_fifo2linear + transfer_linear2fifo. */ - ch->io_fn[0](ch->io_opaque[0], fifo_buf, ch->bytes); - ch->io_fn[1](ch->io_opaque[1], fifo_buf, ch->bytes); -} - struct dma_s { struct soc_dma_s soc; int chnum; @@ -XXX,XX +XXX,XX @@ struct dma_s { enum soc_dma_port_type type; hwaddr addr; union { - struct { - void *opaque; - soc_dma_io_t fn; - int out; - } fifo; struct { void *base; size_t size; @@ -XXX,XX +XXX,XX @@ static inline enum soc_dma_port_type soc_dma_ch_update_type( struct dma_s *dma = (struct dma_s *) ch->dma; struct memmap_entry_s *entry = soc_dma_lookup(dma, ch->vaddr[port]); - if (entry->type == soc_dma_port_fifo) { - while (entry < dma->memmap + dma->memmap_size && - entry->u.fifo.out != port) - entry ++; - if (entry->addr != ch->vaddr[port] || entry->u.fifo.out != port) - return soc_dma_port_other; - - if (ch->type[port] != soc_dma_access_const) - return soc_dma_port_other; - - ch->io_fn[port] = entry->u.fifo.fn; - ch->io_opaque[port] = entry->u.fifo.opaque; - return soc_dma_port_fifo; - } else if (entry->type == soc_dma_port_mem) { + if (entry->type == soc_dma_port_mem) { if (entry->addr > ch->vaddr[port] || entry->addr + entry->u.mem.size <= ch->vaddr[port]) return soc_dma_port_other; @@ -XXX,XX +XXX,XX @@ void soc_dma_ch_update(struct soc_dma_ch_s *ch) } dst = soc_dma_ch_update_type(ch, 1); - /* TODO: use src and dst as array indices. */ if (src == soc_dma_port_mem && dst == soc_dma_port_mem) ch->transfer_fn = transfer_mem2mem; - else if (src == soc_dma_port_mem && dst == soc_dma_port_fifo) - ch->transfer_fn = transfer_mem2fifo; - else if (src == soc_dma_port_fifo && dst == soc_dma_port_mem) - ch->transfer_fn = transfer_fifo2mem; - else if (src == soc_dma_port_fifo && dst == soc_dma_port_fifo) - ch->transfer_fn = transfer_fifo2fifo; else ch->transfer_fn = ch->dma->transfer_fn; @@ -XXX,XX +XXX,XX @@ struct soc_dma_s *soc_dma_init(int n) } soc_dma_reset(&s->soc); - fifo_size = 0; return &s->soc; } -void soc_dma_port_add_fifo(struct soc_dma_s *soc, hwaddr virt_base, - soc_dma_io_t fn, void *opaque, int out) -{ - struct memmap_entry_s *entry; - struct dma_s *dma = (struct dma_s *) soc; - - dma->memmap = g_realloc(dma->memmap, sizeof(*entry) * - (dma->memmap_size + 1)); - entry = soc_dma_lookup(dma, virt_base); - - if (dma->memmap_size) { - if (entry->type == soc_dma_port_mem) { - if (entry->addr <= virt_base && - entry->addr + entry->u.mem.size > virt_base) { - error_report("%s: FIFO at %"PRIx64 - " collides with RAM region at %"PRIx64 - "-%"PRIx64, __func__, - virt_base, entry->addr, - (entry->addr + entry->u.mem.size)); - exit(-1); - } - - if (entry->addr <= virt_base) - entry ++; - } else - while (entry < dma->memmap + dma->memmap_size && - entry->addr <= virt_base) { - if (entry->addr == virt_base && entry->u.fifo.out == out) { - error_report("%s: FIFO at %"PRIx64 - " collides FIFO at %"PRIx64, - __func__, virt_base, entry->addr); - exit(-1); - } - - entry ++; - } - - memmove(entry + 1, entry, - (uint8_t *) (dma->memmap + dma->memmap_size ++) - - (uint8_t *) entry); - } else - dma->memmap_size ++; - - entry->addr = virt_base; - entry->type = soc_dma_port_fifo; - entry->u.fifo.fn = fn; - entry->u.fifo.opaque = opaque; - entry->u.fifo.out = out; -} - void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base, hwaddr virt_base, size_t size) { diff --git a/include/hw/arm/soc_dma.h b/include/hw/arm/soc_dma.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/soc_dma.h +++ b/include/hw/arm/soc_dma.h @@ -XXX,XX +XXX,XX @@ struct soc_dma_s; struct soc_dma_ch_s; -typedef void (*soc_dma_io_t)(void *opaque, uint8_t *buf, int len); typedef void (*soc_dma_transfer_t)(struct soc_dma_ch_s *ch); enum soc_dma_port_type { soc_dma_port_mem, - soc_dma_port_fifo, soc_dma_port_other, }; @@ -XXX,XX +XXX,XX @@ struct soc_dma_ch_s { hwaddr vaddr[2]; /* Updated by .transfer_fn(). */ /* Private */ void *paddr[2]; - soc_dma_io_t io_fn[2]; - void *io_opaque[2]; int running; soc_dma_transfer_t transfer_fn; @@ -XXX,XX +XXX,XX @@ struct soc_dma_s { /* Call to activate or stop a DMA channel. */ void soc_dma_set_request(struct soc_dma_ch_s *ch, int level); -/* Call after every write to one of the following fields and before +/* + * Call after every write to one of the following fields and before * calling soc_dma_set_request(ch, 1): * ch->type[0...1], * ch->vaddr[0...1], * ch->paddr[0...1], - * or after a soc_dma_port_add_fifo() or soc_dma_port_add_mem(). */ + * or after a soc_dma_port_add_mem(). + */ void soc_dma_ch_update(struct soc_dma_ch_s *ch); /* The SoC should call this when the DMA module is being reset. */ void soc_dma_reset(struct soc_dma_s *s); struct soc_dma_s *soc_dma_init(int n); -void soc_dma_port_add_fifo(struct soc_dma_s *dma, hwaddr virt_base, - soc_dma_io_t fn, void *opaque, int out); void soc_dma_port_add_mem(struct soc_dma_s *dma, uint8_t *phys_base, hwaddr virt_base, size_t size); -static inline void soc_dma_port_add_fifo_in(struct soc_dma_s *dma, - hwaddr virt_base, soc_dma_io_t fn, void *opaque) -{ - return soc_dma_port_add_fifo(dma, virt_base, fn, opaque, 0); -} - -static inline void soc_dma_port_add_fifo_out(struct soc_dma_s *dma, - hwaddr virt_base, soc_dma_io_t fn, void *opaque) -{ - return soc_dma_port_add_fifo(dma, virt_base, fn, opaque, 1); -} - #endif -- 2.43.0
Now we only have "mem" and "other" as soc_dma_port_type values, we can simplify soc_dma_ch_update(): either both src and dst are mem, in which case we use transfer_mem2mem and set update to 1 to tell omap_dma_transfer_setup() to update all the guest-visible src/dest/count information to indicate a completed transfer; or else we use the omap_dma_transfer_generic() function, and we set update to 0 to tell omap_dma_transfer_setup() that the transfer function will be updating the src/dest/count. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260710105907.2570621-3-peter.maydell@linaro.org Reviewed-by: Alex Bennée <alex.bennee@linaro.org> --- hw/dma/soc_dma.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/hw/dma/soc_dma.c b/hw/dma/soc_dma.c index XXXXXXX..XXXXXXX 100644 --- a/hw/dma/soc_dma.c +++ b/hw/dma/soc_dma.c @@ -XXX,XX +XXX,XX @@ void soc_dma_ch_update(struct soc_dma_ch_s *ch) enum soc_dma_port_type src, dst; src = soc_dma_ch_update_type(ch, 0); - if (src == soc_dma_port_other) { + dst = soc_dma_ch_update_type(ch, 1); + if (src == soc_dma_port_other || dst == soc_dma_port_other) { ch->update = 0; ch->transfer_fn = ch->dma->transfer_fn; - return; - } - dst = soc_dma_ch_update_type(ch, 1); - - if (src == soc_dma_port_mem && dst == soc_dma_port_mem) + } else { + ch->update = 1; ch->transfer_fn = transfer_mem2mem; - else - ch->transfer_fn = ch->dma->transfer_fn; - - ch->update = (dst != soc_dma_port_other); + } } static void soc_dma_ch_freq_update(struct dma_s *s) -- 2.43.0
There's only one field in the union inside memmap_entry_s now that we've removed the soc_dma_port_fifo handling. Simplify by removing the union. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Message-id: 20260710105907.2570621-4-peter.maydell@linaro.org --- hw/dma/soc_dma.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/hw/dma/soc_dma.c b/hw/dma/soc_dma.c index XXXXXXX..XXXXXXX 100644 --- a/hw/dma/soc_dma.c +++ b/hw/dma/soc_dma.c @@ -XXX,XX +XXX,XX @@ struct dma_s { struct memmap_entry_s { enum soc_dma_port_type type; hwaddr addr; - union { - struct { - void *base; - size_t size; - } mem; - } u; + struct { + void *base; + size_t size; + } mem; } *memmap; int memmap_size; @@ -XXX,XX +XXX,XX @@ static inline enum soc_dma_port_type soc_dma_ch_update_type( if (entry->type == soc_dma_port_mem) { if (entry->addr > ch->vaddr[port] || - entry->addr + entry->u.mem.size <= ch->vaddr[port]) + entry->addr + entry->mem.size <= ch->vaddr[port]) return soc_dma_port_other; /* TODO: support constant memory address for source port as used for @@ -XXX,XX +XXX,XX @@ static inline enum soc_dma_port_type soc_dma_ch_update_type( if (ch->type[port] != soc_dma_access_const) return soc_dma_port_other; - ch->paddr[port] = (uint8_t *) entry->u.mem.base + + ch->paddr[port] = (uint8_t *) entry->mem.base + (ch->vaddr[port] - entry->addr); /* TODO: save bytes left to the end of the mapping somewhere so we * can check we're not reading beyond it. */ @@ -XXX,XX +XXX,XX @@ void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base, if (entry->type == soc_dma_port_mem) { if ((entry->addr >= virt_base && entry->addr < virt_base + size) || (entry->addr <= virt_base && - entry->addr + entry->u.mem.size > virt_base)) { + entry->addr + entry->mem.size > virt_base)) { error_report("%s: RAM at %"PRIx64 "-%"PRIx64 " collides with RAM region at %"PRIx64 "-%"PRIx64, __func__, virt_base, virt_base + size, - entry->addr, entry->addr + entry->u.mem.size); + entry->addr, entry->addr + entry->mem.size); exit(-1); } @@ -XXX,XX +XXX,XX @@ void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base, entry->addr = virt_base; entry->type = soc_dma_port_mem; - entry->u.mem.base = phys_base; - entry->u.mem.size = size; + entry->mem.base = phys_base; + entry->mem.size = size; } /* TODO: port removal for ports like PCMCIA memory */ -- 2.43.0
In omap_dma_transfer_setup(), the maximum number of elements we can transfer is 0xffff * 0xffff == 0xfffe0001 (because the max frame count and max elements per frame are both 65535). However, we store total element counts in 'int' variables, and use INT_MAX as a "bigger than any valid value" sentinel, and when performing arithmetic with the total count of transferred elements we are not careful about avoiding overflows. Fix these: - use uint32_t rather than int for the local variables tracking various element and frame counts - use UINT_MAX as our sentinel - calculate new packet, element and frame counter values using arithmetic on a local uint32_t, rather than doing it in-place on local variables that are only 'int' because the actual counter registers are 16 bits - use 64-bit arithmetic when calculating how much to advance the source and dest pointers and the total dma->bytes transferred Note that since soc_dma_ch_s::bytes is only 'int' this can still overflow; we'll fix that in a subsequent patch. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Jim MacArthur <jim.macarthur@linaro.org> Message-id: 20260710105907.2570621-5-peter.maydell@linaro.org --- hw/dma/omap_dma.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/hw/dma/omap_dma.c b/hw/dma/omap_dma.c index XXXXXXX..XXXXXXX 100644 --- a/hw/dma/omap_dma.c +++ b/hw/dma/omap_dma.c @@ -XXX,XX +XXX,XX @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) struct omap_dma_reg_set_s *a; struct omap_dma_channel_s *ch = dma->opaque; struct omap_dma_s *s = dma->dma->opaque; - int frames, min_elems, elements[__omap_dma_intr_last]; + uint32_t frames, min_elems, elements[__omap_dma_intr_last]; a = &ch->active_set; @@ -XXX,XX +XXX,XX @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) __func__, dma->num); } - min_elems = INT_MAX; + /* + * The maximum frame count and maximum element count are both 0xffff, + * so our worst case possible number of elements to transfer is + * 0xffff * 0xffff == 0xfffe0001. We can therefore keep element + * counts in a uint32_t and use UINT_MAX as a sentinel value for + * "not set" / "condition does not occur". + */ + min_elems = UINT_MAX; /* Check all the conditions that terminate the transfer starting * with those that can occur the soonest. */ @@ -XXX,XX +XXX,XX @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) if (elements[id] < min_elems) \ min_elems = elements[id]; \ } else \ - elements[id] = INT_MAX; + elements[id] = UINT_MAX; /* Elements */ INTR_CHECK( @@ -XXX,XX +XXX,XX @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) (a->frames - a->frame - 1) * a->elements + (a->elements - a->element)) - dma->bytes = min_elems * ch->data_type; + dma->bytes = (uint64_t)min_elems * ch->data_type; /* Set appropriate interrupts and/or deactivate channels */ @@ -XXX,XX +XXX,XX @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) /* Update packet number */ if (ch->fs && ch->bs) { - a->pck_element += min_elems; - a->pck_element %= a->pck_elements; + /* Can't overflow: worst case min_elems 0xFFFE0001 + element 0xFFFF */ + uint32_t new_pck_element = a->pck_element + min_elems; + a->pck_element = new_pck_element % a->pck_elements; } /* @@ -XXX,XX +XXX,XX @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) * can skip part of this. */ if (dma->update) { + /* Can't overflow: worst case min_elems 0xFFFE0001 + element 0xFFFF */ + uint32_t new_element = a->element + min_elems; a->element += min_elems; - frames = a->element / a->elements; - a->element = a->element % a->elements; + frames = new_element / a->elements; + a->element = new_element % a->elements; a->frame += frames; - a->src += min_elems * a->elem_delta[0] + frames * a->frame_delta[0]; - a->dest += min_elems * a->elem_delta[1] + frames * a->frame_delta[1]; + a->src += (uint64_t)min_elems * a->elem_delta[0] + frames * a->frame_delta[0]; + a->dest += (uint64_t)min_elems * a->elem_delta[1] + frames * a->frame_delta[1]; /* If the channel is async, update cpc */ if (!ch->sync && frames) { -- 2.43.0
The worst case number of DMA bytes that omap_dma will ask us to transfer is 0xffff * 0xffff * 4 == 0x3fff80004, which is slightly larger than fits into a uint32_t. Move the byte count to uint64_t, and adjust code that passes it around to also use uint64_t. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Jim MacArthur <jim.macarthur@linaro.org> Message-id: 20260710105907.2570621-6-peter.maydell@linaro.org --- hw/dma/soc_dma.c | 6 +++++- include/hw/arm/soc_dma.h | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/hw/dma/soc_dma.c b/hw/dma/soc_dma.c index XXXXXXX..XXXXXXX 100644 --- a/hw/dma/soc_dma.c +++ b/hw/dma/soc_dma.c @@ -XXX,XX +XXX,XX @@ struct dma_s { struct soc_dma_ch_s ch[]; }; -static void soc_dma_ch_schedule(struct soc_dma_ch_s *ch, int delay_bytes) +static void soc_dma_ch_schedule(struct soc_dma_ch_s *ch, uint64_t delay_bytes) { int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); struct dma_s *dma = (struct dma_s *) ch->dma; + /* + * Worst case delay bytes is only slightly larger than fits into + * a 32-bit integer, so this won't overflow. + */ timer_mod(ch->timer, now + delay_bytes / dma->channel_freq); } diff --git a/include/hw/arm/soc_dma.h b/include/hw/arm/soc_dma.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/soc_dma.h +++ b/include/hw/arm/soc_dma.h @@ -XXX,XX +XXX,XX @@ struct soc_dma_ch_s { int update; /* This should be set by dma->setup_fn(). */ - int bytes; + uint64_t bytes; /* Initialised by the DMA module, call soc_dma_ch_update after writing. */ enum soc_dma_access_type type[2]; hwaddr vaddr[2]; /* Updated by .transfer_fn(). */ -- 2.43.0
The soc_dma code has a fastpath for when DMA transfers are from RAM to RAM. The current implementation of this has the caller of soc_dma_port_add_mem() pass the underlying host address of the RAM block that the DMA port is connected to (obtained via memory_region_get_ram_ptr()). Then the actual transfer function does a simple memcpy(). This has several problems. Most importantly, no bounds checking is done on the address and size passed by the guest, so the memcpy source and destination might be outside the backing host RAM entirely. Secondly, because the DMA access is done via this back door, there is no updating of the dirty region when memory is written this way (there is a TODO comment in omap_dma.c noting this). Fix both of these by making the memory to memory transfer function use physical_memory_map() to get the host addresses for the memory copy. That function will automatically give us the bounds check that we want and return a short length if the transfer would run off the end of the RAM MemoryRegion it starts in. Since the OMAP DMA documentation states that it's a guest error to misprogram the addresses so that they fall outside the range that is valid for the particular DMA port being addressed and that this can result in guest memory corruption , we don't need to loop for short transfers, but can simply log them and continue. Note that we don't need to update addresses or bytecount here in the transfer function, because when soc_dma_ch_update() selects transfer_mem2mem it also sets ch->update to 1, which tells the omap_dma_transfer_setup() code that it is responsible for updating all the guest visible fields to match "transfer completed". (We use physical_memory_map() here to match the use of physical_memory_read() and physical_memory_write() in omap_dma.c; making the DMA controller use an explicit AddressSpace would be a separate cleanup task.) Together with the preceding commits that fixed some integer overflow problems, this fixes the "guest can provoke a bad memcpy() operation" reported in issue #3204. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3204 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Message-id: 20260710105907.2570621-7-peter.maydell@linaro.org --- hw/dma/omap_dma.c | 5 ----- hw/dma/soc_dma.c | 51 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/hw/dma/omap_dma.c b/hw/dma/omap_dma.c index XXXXXXX..XXXXXXX 100644 --- a/hw/dma/omap_dma.c +++ b/hw/dma/omap_dma.c @@ -XXX,XX +XXX,XX @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) if (!ch->sync && frames) { ch->cpc = a->dest & 0xffff; } - - /* - * TODO: if the destination port is IMIF or EMIFF, set the dirty - * bits on it. - */ } omap_dma_interrupts_update(s); diff --git a/hw/dma/soc_dma.c b/hw/dma/soc_dma.c index XXXXXXX..XXXXXXX 100644 --- a/hw/dma/soc_dma.c +++ b/hw/dma/soc_dma.c @@ -XXX,XX +XXX,XX @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/timer.h" +#include "qemu/log.h" +#include "system/physmem.h" #include "hw/arm/soc_dma.h" static void transfer_mem2mem(struct soc_dma_ch_s *ch) { - memcpy(ch->paddr[0], ch->paddr[1], ch->bytes); - ch->paddr[0] += ch->bytes; - ch->paddr[1] += ch->bytes; + /* + * Memory-to-memory transfer: do the whole thing in one go. The + * hardware spec says that it is invalid to program the OMAP DMA + * controller with addresses that don't match the port (i.e. to + * ask for a transfer to/from a memory port with a physaddr that + * isn't within that port range) and that if you do then the + * transfer continues and memory can be corrupted. So we can map + * both source and destination, and treat short mappings and + * failed mappings as a guest error. + */ + hwaddr srclen = ch->bytes; + hwaddr dstlen = ch->bytes; + hwaddr srcaddr = ch->vaddr[0]; + hwaddr dstaddr = ch->vaddr[1]; + void *srcmem, *dstmem; + hwaddr xferlen = 0; + + srcmem = physical_memory_map(srcaddr, &srclen, false); + if (!srcmem) { + qemu_log_mask(LOG_GUEST_ERROR, + "soc_dma mem2mem transfer: could not map source; " + "guest error programming source port/address\n"); + return; + } + + dstmem = physical_memory_map(dstaddr, &dstlen, true); + if (!dstmem) { + qemu_log_mask(LOG_GUEST_ERROR, + "soc_dma mem2mem transfer: could not map destination; " + "guest error programming destination port/address\n"); + goto unmap_src; + } + + xferlen = MIN(srclen, dstlen); + if (xferlen < ch->bytes) { + qemu_log_mask(LOG_GUEST_ERROR, + "soc_dma mem2mem transfer: could not transfer all data; " + "guest error programming src or destination addresses\n"); + /* Continue to transfer whatever did fit in the port window */ + } + + memmove(dstmem, srcmem, xferlen); + + physical_memory_unmap(dstmem, dstlen, true, xferlen); +unmap_src: + physical_memory_unmap(srcmem, srclen, false, xferlen); } struct dma_s { -- 2.43.0
Now that transfer_mem2mem() uses physical_memory_map(), the soc_dma_ch_s::paddr field is unused; remove it, and the code that set it, and the memmap_entry_s::mem.base and the soc_dma_port_add_mem() phys_base argument that were passing around host pointers to use for setting paddr. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Message-id: 20260710105907.2570621-8-peter.maydell@linaro.org --- hw/arm/omap1.c | 6 ++---- hw/dma/soc_dma.c | 9 +-------- include/hw/arm/soc_dma.h | 7 ++----- 3 files changed, 5 insertions(+), 17 deletions(-) diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -XXX,XX +XXX,XX @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *dram, s->port[tipb_mpui].addr_valid = omap_validate_tipb_mpui_addr; /* Register SDRAM and SRAM DMA ports for fast transfers. */ - soc_dma_port_add_mem(s->dma, memory_region_get_ram_ptr(dram), - OMAP_EMIFF_BASE, s->sdram_size); - soc_dma_port_add_mem(s->dma, memory_region_get_ram_ptr(&s->imif_ram), - OMAP_IMIF_BASE, s->sram_size); + soc_dma_port_add_mem(s->dma, OMAP_EMIFF_BASE, s->sdram_size); + soc_dma_port_add_mem(s->dma, OMAP_IMIF_BASE, s->sram_size); s->timer[0] = omap_mpu_timer_init(system_memory, 0xfffec500, qdev_get_gpio_in(s->ih[0], OMAP_INT_TIMER1), diff --git a/hw/dma/soc_dma.c b/hw/dma/soc_dma.c index XXXXXXX..XXXXXXX 100644 --- a/hw/dma/soc_dma.c +++ b/hw/dma/soc_dma.c @@ -XXX,XX +XXX,XX @@ struct dma_s { enum soc_dma_port_type type; hwaddr addr; struct { - void *base; size_t size; } mem; } *memmap; @@ -XXX,XX +XXX,XX @@ static inline enum soc_dma_port_type soc_dma_ch_update_type( if (ch->type[port] != soc_dma_access_const) return soc_dma_port_other; - ch->paddr[port] = (uint8_t *) entry->mem.base + - (ch->vaddr[port] - entry->addr); - /* TODO: save bytes left to the end of the mapping somewhere so we - * can check we're not reading beyond it. */ return soc_dma_port_mem; } else return soc_dma_port_other; @@ -XXX,XX +XXX,XX @@ struct soc_dma_s *soc_dma_init(int n) return &s->soc; } -void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base, - hwaddr virt_base, size_t size) +void soc_dma_port_add_mem(struct soc_dma_s *soc, hwaddr virt_base, size_t size) { struct memmap_entry_s *entry; struct dma_s *dma = (struct dma_s *) soc; @@ -XXX,XX +XXX,XX @@ void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base, entry->addr = virt_base; entry->type = soc_dma_port_mem; - entry->mem.base = phys_base; entry->mem.size = size; } diff --git a/include/hw/arm/soc_dma.h b/include/hw/arm/soc_dma.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/soc_dma.h +++ b/include/hw/arm/soc_dma.h @@ -XXX,XX +XXX,XX @@ struct soc_dma_ch_s { /* Initialised by the DMA module, call soc_dma_ch_update after writing. */ enum soc_dma_access_type type[2]; hwaddr vaddr[2]; /* Updated by .transfer_fn(). */ - /* Private */ - void *paddr[2]; int running; soc_dma_transfer_t transfer_fn; @@ -XXX,XX +XXX,XX @@ void soc_dma_set_request(struct soc_dma_ch_s *ch, int level); * calling soc_dma_set_request(ch, 1): * ch->type[0...1], * ch->vaddr[0...1], - * ch->paddr[0...1], * or after a soc_dma_port_add_mem(). */ void soc_dma_ch_update(struct soc_dma_ch_s *ch); @@ -XXX,XX +XXX,XX @@ void soc_dma_ch_update(struct soc_dma_ch_s *ch); void soc_dma_reset(struct soc_dma_s *s); struct soc_dma_s *soc_dma_init(int n); -void soc_dma_port_add_mem(struct soc_dma_s *dma, uint8_t *phys_base, - hwaddr virt_base, size_t size); +void soc_dma_port_add_mem(struct soc_dma_s *dma, + hwaddr virt_base, size_t size); #endif -- 2.43.0
omap_dma.h is the header file for hw/dma/omap_dma.c; it fits better to put it in include/hw/dma/ to match where we have the .c file. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Message-id: 20260710105907.2570621-9-peter.maydell@linaro.org --- hw/arm/omap1.c | 2 +- hw/dma/omap_dma.c | 2 +- hw/dma/soc_dma.c | 2 +- include/hw/{arm => dma}/soc_dma.h | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename include/hw/{arm => dma}/soc_dma.h (100%) diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -XXX,XX +XXX,XX @@ #include "hw/sd/sd.h" #include "system/blockdev.h" #include "system/system.h" -#include "hw/arm/soc_dma.h" +#include "hw/dma/soc_dma.h" #include "system/qtest.h" #include "system/reset.h" #include "system/runstate.h" diff --git a/hw/dma/omap_dma.c b/hw/dma/omap_dma.c index XXXXXXX..XXXXXXX 100644 --- a/hw/dma/omap_dma.c +++ b/hw/dma/omap_dma.c @@ -XXX,XX +XXX,XX @@ #include "qemu/timer.h" #include "hw/arm/omap.h" #include "hw/core/irq.h" -#include "hw/arm/soc_dma.h" +#include "hw/dma/soc_dma.h" #include "system/physmem.h" struct omap_dma_channel_s { diff --git a/hw/dma/soc_dma.c b/hw/dma/soc_dma.c index XXXXXXX..XXXXXXX 100644 --- a/hw/dma/soc_dma.c +++ b/hw/dma/soc_dma.c @@ -XXX,XX +XXX,XX @@ #include "qemu/timer.h" #include "qemu/log.h" #include "system/physmem.h" -#include "hw/arm/soc_dma.h" +#include "hw/dma/soc_dma.h" static void transfer_mem2mem(struct soc_dma_ch_s *ch) { diff --git a/include/hw/arm/soc_dma.h b/include/hw/dma/soc_dma.h similarity index 100% rename from include/hw/arm/soc_dma.h rename to include/hw/dma/soc_dma.h -- 2.43.0
The hw/dma/soc_dma.c code appears to have been written with the idea that it abstracts out DMA transfer operations from the details of a particular DMA controller device. In practice, it's used only by the omap_dma code and I would not today recommend trying to use it in any new DMA device. Add the files to the OMAP section of MAINTAINERS so that patches can be cc'd to the appropriate places. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Message-id: 20260710105907.2570621-10-peter.maydell@linaro.org --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ M: Peter Maydell <peter.maydell@linaro.org> L: qemu-arm@nongnu.org S: Odd Fixes F: hw/*/omap* +F: hw/dma/soc_dma.c F: include/hw/arm/omap.h +F: include/hw/dma/soc_dma.h F: docs/system/arm/sx1.rst F: tests/functional/arm/test_sx1.py -- 2.43.0
In c8bea1276c we moved cpu32.c and built it as a common object. The commit message says "cpu32.c only contains CPU types used in 32-bit system emulation". However, this is incorrect -- it contains 32-bit CPU types used in both system and usermode emulation. (The case where we don't need these CPUs and which we were using ifdefs to avoid is specifically the AArch64 usermode qemu-aarch64 binary.) The effect is that qemu-arm lost all the named CPU types except the M-profile ones (which are in cpu-v7m.c). This reverts commit c8bea1276c85855b2e088b740737d043c8556d58. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3962 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> Message-id: 20260720100312.119894-1-peter.maydell@linaro.org --- target/arm/tcg/{cpu32-system.c => cpu32.c} | 10 +++++++++- target/arm/tcg/meson.build | 5 ++++- 2 files changed, 13 insertions(+), 2 deletions(-) rename target/arm/tcg/{cpu32-system.c => cpu32.c} (99%) diff --git a/target/arm/tcg/cpu32-system.c b/target/arm/tcg/cpu32.c similarity index 99% rename from target/arm/tcg/cpu32-system.c rename to target/arm/tcg/cpu32.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/cpu32-system.c +++ b/target/arm/tcg/cpu32.c @@ -XXX,XX +XXX,XX @@ /* - * QEMU ARM TCG-only CPUs (not needed for the AArch64 linux-user build) + * QEMU ARM TCG-only CPUs. * * Copyright (c) 2012 SUSE LINUX Products GmbH * @@ -XXX,XX +XXX,XX @@ #include "cpu.h" #include "accel/tcg/cpu-ops.h" #include "internals.h" +#if !defined(CONFIG_USER_ONLY) #include "hw/core/boards.h" +#endif #include "cpregs.h" + +/* CPU models. These are not needed for the AArch64 linux-user build. */ +#if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64) + static void arm926_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -XXX,XX +XXX,XX @@ static void arm_tcg_cpu_register_types(void) } type_init(arm_tcg_cpu_register_types) + +#endif /* !CONFIG_USER_ONLY || !TARGET_AARCH64 */ diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -XXX,XX +XXX,XX @@ translate32_d = [ arm_ss.add(when: 'TARGET_AARCH64', if_true: gen_a64) arm_stubs_ss.add(files('stubs32.c')) +arm_ss.add(files( + 'cpu32.c', +)) + arm_ss.add(when: 'TARGET_AARCH64', if_true: files( 'gengvec64.c', 'translate-a64.c', @@ -XXX,XX +XXX,XX @@ arm_common_user_system_ss.add(when: 'TARGET_AARCH64', if_true: files( arm_common_system_ss.add(files( 'cpregs-at.c', - 'cpu32-system.c', 'gicv5-cpuif.c', 'psci.c', 'tlb_helper.c', -- 2.43.0
v3->v4: Windows headers define an INT type which clashed with an enum value name in arm_gicv3_its.c... The following changes since commit eae587e8e3694b1aceab23239493fb4c7e1a80f5: Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2021-09-13' into staging (2021-09-13 11:00:30 +0100) are available in the Git repository at: https://git.linaro.org/people/pmaydell/qemu-arm.git tags/pull-target-arm-20210913-3 for you to fetch changes up to 28e987a7e7edaa3ca7feeac65edca26145df8814: hw/arm/mps2.c: Mark internal-only I2C buses as 'full' (2021-09-13 21:01:08 +0100) ---------------------------------------------------------------- target-arm queue: * mark MPS2/MPS3 board-internal i2c buses as 'full' so that command line user-created devices are not plugged into them * Take an exception if PSTATE.IL is set * Support an emulated ITS in the virt board * Add support for kudo-bmc board * Probe for KVM_CAP_ARM_VM_IPA_SIZE when creating scratch VM * cadence_uart: Fix clock handling issues that prevented u-boot from running ---------------------------------------------------------------- Bin Meng (6): hw/misc: zynq_slcr: Correctly compute output clocks in the reset exit phase hw/char: cadence_uart: Disable transmit when input clock is disabled hw/char: cadence_uart: Move clock/reset check to uart_can_receive() hw/char: cadence_uart: Convert to memop_with_attrs() ops hw/char: cadence_uart: Ignore access when unclocked or in reset for uart_{read, write}() hw/char: cadence_uart: Log a guest error when device is unclocked or in reset Chris Rauer (1): hw/arm: Add support for kudo-bmc board. Marc Zyngier (1): hw/arm/virt: KVM: Probe for KVM_CAP_ARM_VM_IPA_SIZE when creating scratch VM Peter Maydell (5): target/arm: Take an exception if PSTATE.IL is set qdev: Support marking individual buses as 'full' hw/arm/mps2-tz.c: Add extra data parameter to MakeDevFn hw/arm/mps2-tz.c: Mark internal-only I2C buses as 'full' hw/arm/mps2.c: Mark internal-only I2C buses as 'full' Richard Henderson (1): target/arm: Merge disas_a64_insn into aarch64_tr_translate_insn Shashi Mallela (9): hw/intc: GICv3 ITS initial framework hw/intc: GICv3 ITS register definitions added hw/intc: GICv3 ITS command queue framework hw/intc: GICv3 ITS Command processing hw/intc: GICv3 ITS Feature enablement hw/intc: GICv3 redistributor ITS processing tests/data/acpi/virt: Add IORT files for ITS hw/arm/virt: add ITS support in virt GIC tests/data/acpi/virt: Update IORT files for ITS docs/system/arm/nuvoton.rst | 1 + hw/intc/gicv3_internal.h | 188 ++++- include/hw/arm/virt.h | 2 + include/hw/intc/arm_gicv3_common.h | 13 + include/hw/intc/arm_gicv3_its_common.h | 32 +- include/hw/qdev-core.h | 24 + target/arm/cpu.h | 1 + target/arm/kvm_arm.h | 4 +- target/arm/syndrome.h | 5 + target/arm/translate.h | 2 + hw/arm/mps2-tz.c | 92 ++- hw/arm/mps2.c | 12 +- hw/arm/npcm7xx_boards.c | 34 + hw/arm/virt.c | 29 +- hw/char/cadence_uart.c | 61 +- hw/intc/arm_gicv3.c | 14 + hw/intc/arm_gicv3_common.c | 13 + hw/intc/arm_gicv3_cpuif.c | 7 +- hw/intc/arm_gicv3_dist.c | 5 +- hw/intc/arm_gicv3_its.c | 1322 ++++++++++++++++++++++++++++++++ hw/intc/arm_gicv3_its_common.c | 7 +- hw/intc/arm_gicv3_its_kvm.c | 2 +- hw/intc/arm_gicv3_redist.c | 153 +++- hw/misc/zynq_slcr.c | 31 +- softmmu/qdev-monitor.c | 7 +- target/arm/helper-a64.c | 1 + target/arm/helper.c | 8 + target/arm/kvm.c | 7 +- target/arm/translate-a64.c | 255 +++--- target/arm/translate.c | 21 + hw/intc/meson.build | 1 + tests/data/acpi/virt/IORT | Bin 0 -> 124 bytes tests/data/acpi/virt/IORT.memhp | Bin 0 -> 124 bytes tests/data/acpi/virt/IORT.numamem | Bin 0 -> 124 bytes tests/data/acpi/virt/IORT.pxb | Bin 0 -> 124 bytes 35 files changed, 2144 insertions(+), 210 deletions(-) create mode 100644 hw/intc/arm_gicv3_its.c create mode 100644 tests/data/acpi/virt/IORT create mode 100644 tests/data/acpi/virt/IORT.memhp create mode 100644 tests/data/acpi/virt/IORT.numamem create mode 100644 tests/data/acpi/virt/IORT.pxb