:p
atchew
Login
The following changes since commit 94826ec1370328375c3b6d1e80fdc94c8f46c348: Merge tag 'accel-20260706' of https://github.com/philmd/qemu into staging (2026-07-06 18:38:14 +0200) are available in the Git repository at: https://github.com/philmd/qemu.git tags/hw-misc-20260707 for you to fetch changes up to 04584a27dd59741bc20272c6faab96c08bcbb926: Revert "aspeed/smc: snoop SPI transfers to fake dummy cycles" (2026-07-07 15:08:41 +0200) ---------------------------------------------------------------- Misc HW patches - Fix in few trace event formats - A pair of improvements in util/ - FlexCAN3 to imx8mp-evk board - Various fixes in hw/ (EDU, ATI VGA, IDE AHCI, PCA9552, i8257 DMA, e1000e/igb, MPT SAS, Hyper-V, QXL, M25P80) ---------------------------------------------------------------- BALATON Zoltan (1): hw/display/ati: Include vga state in the blit context Bernhard Beschow (5): hw/net/can/flexcan: Wire clock control module via link property hw/net/can/flexcan: Subclass TYPE_CAN_FLEXCAN hw/arm/imx8mp-evk: Open code DEFINE_MACHINE_AARCH64 hw/arm/imx8mp-evk: Introduce FslImx8mpEvkState hw/arm: Add basic FlexCAN3 support to TYPE_FSL_IMX8MP and imx8mp-evk Bin Meng (8): hw/block: m25p80: Fix dummy byte handling for Winbond flash hw/block: m25p80: Fix dummy byte handling for Numonyx/Micron flash hw/block: m25p80: Fix dummy byte handling for Macronix flash hw/ssi: npcm7xx_fiu: Correct the dummy cycle emulation logic hw/ssi: xilinx_spips: Fix dummy phase handling hw/ssi: aspeed_smc: Fix direct-read dummy bytes Revert "aspeed/smc: Fix number of dummy cycles for FAST_READ_4 command" Revert "aspeed/smc: snoop SPI transfers to fake dummy cycles" Denis V. Lunev (1): hw/ide/ahci: cancel in-flight buffered reads on command engine restart Dmitry Frolov (1): utils/module: fix memleak in module_load() Evgeny Kolmakov (1): util/filemonitor-inotify: Use QEMU_LOCK_GUARD() Junjie Cao (2): hw/display/ati: reset host_data.next in write handler after flush hw/display/ati: guard against zero bpp in ati_host_data_flush Mark Cave-Ayland (2): hw/rtc/mc146818rtc: convert date from object prop to class prop hw/acpi/ich9: move initial property values into ich9_reset_properties() Peter Maydell (3): hw/dma/i8257: Return zeroes for read_memory in verify mode hw/scsi/mptsas: Reset doorbell state on reset hw/hyperv: Avoid crash if hyperv_find_cpu() passed invalid vp_index Sanjeeva Yerrapureddy (1): hw/net: fix e1000e/igb ip_len inflation by Ethernet minimum-frame padding Shameer Kolothum (1): backends/iommufd: Fix dev_id and type order in viommu trace Song Gao (1): hw/intc/loongarch_dintc: Fix OOB access in DINT MMIO write handler Thomas Huth (1): hw/display/qxl: Fix mono cursor validation that can read past a cursor chunk Torin Carey (1): hw/misc/edu: restrict dma access to dma buffer yujun (3): hw/gpio/pca9552: fix off-by-one in QOM led index validation ebpf: fix swapped toeplitz/indirection args in set_data trace crypto/x509-utils: fix gnutls error code in crt_init failure path docs/system/arm/imx8m.rst | 1 + include/hw/acpi/ich9.h | 2 + include/hw/arm/fsl-imx8mp.h | 8 ++ include/hw/net/flexcan.h | 4 +- include/hw/ssi/aspeed_smc.h | 2 - include/hw/ssi/xilinx_spips.h | 2 +- crypto/x509-utils.c | 3 +- ebpf/ebpf_rss.c | 2 +- hw/acpi/ich9.c | 8 +- hw/arm/fsl-imx6.c | 5 +- hw/arm/fsl-imx8mp.c | 38 ++++++++++ hw/arm/imx8mp-evk.c | 64 +++++++++++++--- hw/block/m25p80.c | 122 +++++++++++++++++++----------- hw/display/ati.c | 2 + hw/display/ati_2d.c | 44 +++++------ hw/display/qxl-render.c | 8 +- hw/dma/i8257.c | 13 ++++ hw/gpio/pca9552.c | 8 +- hw/hyperv/hyperv.c | 4 +- hw/ide/ahci.c | 3 + hw/intc/loongarch_dintc.c | 13 ++++ hw/isa/lpc_ich9.c | 1 + hw/misc/edu.c | 24 +++--- hw/misc/imx8mp_ccm.c | 3 + hw/net/can/flexcan.c | 90 ++++++++++++++++++----- hw/net/net_tx_pkt.c | 7 +- hw/rtc/mc146818rtc.c | 4 +- hw/scsi/mptsas.c | 4 + hw/ssi/aspeed_smc.c | 135 +++++----------------------------- hw/ssi/npcm7xx_fiu.c | 25 ++----- hw/ssi/xilinx_spips.c | 123 ++++++++++++++++++++++--------- util/filemonitor-inotify.c | 28 ++----- util/module.c | 2 +- backends/trace-events | 2 +- hw/arm/Kconfig | 1 + hw/ssi/trace-events | 1 - 36 files changed, 488 insertions(+), 318 deletions(-) -- 2.53.0
From: Torin Carey <torin@tcarey.uk> The EDU device doesn't enforce any bound checks on the addresses provided, allowing users of the device to perform arbitrary reads and writes to QEMU's address space. Signed-off-by: Torin Carey <torin@tcarey.uk> Cc: qemu-stable@nongnu.org Fixes: 7b608e5d6c1 ("hw: misc: edu: use qemu_log_mask instead of hw_error") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3852 Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-ID: <aQtAotYvzFY0Vpft@tcarey.uk> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- hw/misc/edu.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/hw/misc/edu.c b/hw/misc/edu.c index XXXXXXX..XXXXXXX 100644 --- a/hw/misc/edu.c +++ b/hw/misc/edu.c @@ -XXX,XX +XXX,XX @@ static void edu_lower_irq(EduState *edu, uint32_t val) } } -static void edu_check_range(uint64_t xfer_start, uint64_t xfer_size, +static bool edu_check_range(uint64_t xfer_start, uint64_t xfer_size, uint64_t dma_start, uint64_t dma_size) { uint64_t xfer_end = xfer_start + xfer_size; @@ -XXX,XX +XXX,XX @@ static void edu_check_range(uint64_t xfer_start, uint64_t xfer_size, */ if (dma_end >= dma_start && xfer_end >= xfer_start && xfer_start >= dma_start && xfer_end <= dma_end) { - return; + return true; } qemu_log_mask(LOG_GUEST_ERROR, "EDU: DMA range 0x%016"PRIx64"-0x%016"PRIx64 " out of bounds (0x%016"PRIx64"-0x%016"PRIx64")!", xfer_start, xfer_end - 1, dma_start, dma_end - 1); + + return false; } static dma_addr_t edu_clamp_addr(const EduState *edu, dma_addr_t addr) @@ -XXX,XX +XXX,XX @@ static void edu_dma_timer(void *opaque) if (EDU_DMA_DIR(edu->dma.cmd) == EDU_DMA_FROM_PCI) { uint64_t dst = edu->dma.dst; - edu_check_range(dst, edu->dma.cnt, DMA_START, DMA_SIZE); - dst -= DMA_START; - pci_dma_read(&edu->pdev, edu_clamp_addr(edu, edu->dma.src), - edu->dma_buf + dst, edu->dma.cnt); + if (edu_check_range(dst, edu->dma.cnt, DMA_START, DMA_SIZE)) { + dst -= DMA_START; + pci_dma_read(&edu->pdev, edu_clamp_addr(edu, edu->dma.src), + edu->dma_buf + dst, edu->dma.cnt); + } } else { uint64_t src = edu->dma.src; - edu_check_range(src, edu->dma.cnt, DMA_START, DMA_SIZE); - src -= DMA_START; - pci_dma_write(&edu->pdev, edu_clamp_addr(edu, edu->dma.dst), - edu->dma_buf + src, edu->dma.cnt); + if (edu_check_range(src, edu->dma.cnt, DMA_START, DMA_SIZE)) { + src -= DMA_START; + pci_dma_write(&edu->pdev, edu_clamp_addr(edu, edu->dma.dst), + edu->dma_buf + src, edu->dma.cnt); + } } edu->dma.cmd &= ~EDU_DMA_RUN; -- 2.53.0
From: Junjie Cao <junjie.cao@intel.com> ati_host_data_flush() resets host_data.next only on its success path. When it returns early (unsupported bpp, direction, src_source, or src_datatype), next remains stale at >= 4. The next HOST_DATA write then stores a guest-controlled dword at acc[4+], overflowing the 4-element accumulator array. Fix this by resetting next unconditionally in the write handler after calling ati_host_data_flush() or ati_host_data_finish(), and removing the reset from inside ati_host_data_flush(). This ensures the write handler owns the full lifecycle of the accumulator index regardless of flush success or failure. Cc: qemu-stable@nongnu.org Reported-by: Feifan Qian <bea1e@proton.me> Resolves: https://lore.kernel.org/qemu-devel/Czyl6yVfL6sHl_o1kRk8N_LpwXMMRVhO9vgz1qCVJFagn9D4nHSKuiux39iOLty0Q3acxQq_FeovPhTQvSKus2htwjI9lTajLZmqovr0Wxs=@proton.me/ Signed-off-by: Junjie Cao <junjie.cao@intel.com> Reviewed-by: Chad Jablonski <chad@jablonski.xyz> Message-ID: <20260519023937.439077-2-junjie.cao@intel.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- hw/display/ati.c | 2 ++ hw/display/ati_2d.c | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/display/ati.c b/hw/display/ati.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -XXX,XX +XXX,XX @@ static void ati_mm_write(void *opaque, hwaddr addr, s->host_data.acc[s->host_data.next++] = data; if (addr == HOST_DATA_LAST) { ati_host_data_finish(s); + s->host_data.next = 0; } else if (s->host_data.next >= 4) { ati_host_data_flush(s); + s->host_data.next = 0; } break; default: diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/ati_2d.c +++ b/hw/display/ati_2d.c @@ -XXX,XX +XXX,XX @@ bool ati_host_data_flush(ATIVGAState *s) } /* Track state of the overall blit for use by the next flush */ - s->host_data.next = 0; s->host_data.row = row; s->host_data.col = col; if (s->host_data.row >= ctx.dst.height) { -- 2.53.0
From: Junjie Cao <junjie.cao@intel.com> ati_bpp_from_datatype() returns 0 for unrecognized dp_datatype nibble values (0, 1, or >= 7). ati_host_data_flush() only guards against the bpp == 24 case but not bpp == 0, leading to: 1. Division by zero at "pix_count /= ctx.bpp" (SIGFPE) when src_datatype is SRC_COLOR. 2. g_assert_not_reached() in stn_he_p() when bypp (= bpp/8 = 0) hits the default case of the size switch. Both are guest-triggerable via MMIO writes to the dp_datatype register while a HOST_DATA blit is active. Add an explicit bpp == 0 check with LOG_GUEST_ERROR before proceeding with the blit, consistent with the existing check in ati_2d_do_blt(). Cc: qemu-stable@nongnu.org Reported-by: Feifan Qian <bea1e@proton.me> Signed-off-by: Junjie Cao <junjie.cao@intel.com> Reviewed-by: Chad Jablonski <chad@jablonski.xyz> Message-ID: <20260519023937.439077-3-junjie.cao@intel.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- hw/display/ati_2d.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/ati_2d.c +++ b/hw/display/ati_2d.c @@ -XXX,XX +XXX,XX @@ bool ati_host_data_flush(ATIVGAState *s) setup_2d_blt_ctx(s, &ctx); + if (!ctx.bpp) { + qemu_log_mask(LOG_GUEST_ERROR, + "host_data_blt: invalid bpp from datatype\n"); + return false; + } if (ctx.bpp == 24) { qemu_log_mask(LOG_UNIMP, "host_data_blt: unsupported in 24 bits mode\n"); -- 2.53.0
From: "Denis V. Lunev" <den@openvz.org> ATAPI CD reads are issued through ide_buffered_readv() (cd_read_sector() and ide_atapi_cmd_read_dma_cb() in hw/ide/atapi.c). The PIO path discards the returned aiocb; the DMA path stores it in s->bus->dma->aiocb. A guest can stop and restart a port's command engine (PxCMD.ST 1 -> 0 -> 1) while such a read is still in flight. Stopping the engine unmaps the command list (ahci_unmap_clb_address()) and restarting it re-maps the list and clears AHCIDevice.cur_cmd to NULL, but nothing tears down the outstanding read. This path does not run ide_reset(), so the drive's transfer state is preserved and the read still completes. Its callbacks then dereference the stale or NULL cur_cmd in the AHCI transfer helpers: PIO: cd_read_sector_cb() -> ide_atapi_cmd_reply_end() -> ide_transfer_start_norecurse() -> ahci_pio_transfer() DMA: ide_atapi_cmd_read_dma_cb() -> ahci_dma_rw_buf() -> ahci_populate_sglist() Both crash with a NULL cur_cmd; the PIO variant has been seen in the field. Cancel the outstanding I/O when the command list is unmapped, reusing ide_cancel_dma_sync() as the ATAPI DEVICE RESET command does. It runs the completion callback with -ECANCELED (which tears down s->bus->dma->aiocb for the DMA case) and orphans the buffered request, so the eventual asynchronous completion is a no-op. Merely setting the orphaned flag is not enough: it would leave s->bus->dma->aiocb pointing at a freed aiocb that a later reset would cancel. Fixes: 1d8c11d63154 ("ide: add support for IDEBufferedRequest") Signed-off-by: Denis V. Lunev <den@openvz.org> Message-ID: <20260619112158.304782-2-den@openvz.org> [PMD: Use ide_bus_active_if()] Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- hw/ide/ahci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index XXXXXXX..XXXXXXX 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -XXX,XX +XXX,XX @@ static bool ahci_map_clb_address(AHCIDevice *ad) static void ahci_unmap_clb_address(AHCIDevice *ad) { + /* Cancel in-flight reads that would complete against a cleared cur_cmd. */ + ide_cancel_dma_sync(ide_bus_active_if(&ad->port)); + if (ad->lst == NULL) { trace_ahci_unmap_clb_address_null(ad->hba, ad->port_no); return; -- 2.53.0
From: BALATON Zoltan <balaton@eik.bme.hu> The vga state is needed by several functions using the blit context so just include it in the context instead of passing it separately. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Reviewed-by: Chad Jablonski <chad@jablonski.xyz> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Message-ID: <20260619140408.6CF98596948@zero.eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- hw/display/ati_2d.c | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/ati_2d.c +++ b/hw/display/ati_2d.c @@ -XXX,XX +XXX,XX @@ static int ati_bpp_from_datatype(const ATIVGAState *s) } typedef struct { + VGACommonState *vga; int bpp; uint32_t rop3; bool host_data_active; @@ -XXX,XX +XXX,XX @@ typedef struct { bool top_to_bottom; bool need_swap; uint32_t frgd_clr; - const uint8_t *palette; - const uint8_t *vram_end; QemuRect scissor; QemuRect dst; @@ -XXX,XX +XXX,XX @@ typedef struct { const uint8_t *src_bits; } ATI2DCtx; -static void ati_set_dirty(VGACommonState *vga, const ATI2DCtx *ctx) +static void ati_set_dirty(const ATI2DCtx *ctx) { + VGACommonState *vga = ctx->vga; DisplaySurface *ds = qemu_console_surface(vga->con); unsigned int bypp = ctx->bpp / 8; hwaddr dirty_start = ctx->dst_offset + ctx->dst.x * bypp + @@ -XXX,XX +XXX,XX @@ static void ati_set_dirty(VGACommonState *vga, const ATI2DCtx *ctx) } } -static void setup_2d_blt_ctx(const ATIVGAState *s, ATI2DCtx *ctx) +static void setup_2d_blt_ctx(ATIVGAState *s, ATI2DCtx *ctx) { + ctx->vga = &s->vga; ctx->bpp = ati_bpp_from_datatype(s); ctx->rop3 = s->regs.dp_mix & GMC_ROP3_MASK; ctx->host_data_active = s->host_data.active; @@ -XXX,XX +XXX,XX @@ static void setup_2d_blt_ctx(const ATIVGAState *s, ATI2DCtx *ctx) ctx->top_to_bottom = s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM; ctx->need_swap = (HOST_BIG_ENDIAN != s->vga.big_endian_fb); ctx->frgd_clr = s->regs.dp_brush_frgd_clr; - ctx->palette = s->vga.palette; ctx->dst_offset = s->regs.dst_offset; - ctx->vram_end = s->vga.vram_ptr + s->vga.vram_size; ctx->scissor.width = s->regs.sc_right - s->regs.sc_left + 1; ctx->scissor.height = s->regs.sc_bottom - s->regs.sc_top + 1; @@ -XXX,XX +XXX,XX @@ static uint32_t make_filler(int bpp, uint32_t color) return color; } -static bool ati_2d_do_blt(ATI2DCtx *ctx, uint8_t use_pixman) +static bool ati_2d_do_blt(const ATI2DCtx *ctx, uint8_t use_pixman) { QemuRect vis_src, vis_dst; unsigned int x, y, i, j, bypp = ctx->bpp / 8; + const uint8_t *vram_end = ctx->vga->vram_ptr + ctx->vga->vram_size; if (!ctx->bpp) { qemu_log_mask(LOG_GUEST_ERROR, "Invalid bpp\n"); @@ -XXX,XX +XXX,XX @@ static bool ati_2d_do_blt(ATI2DCtx *ctx, uint8_t use_pixman) return false; } if (ctx->dst.x > 0x3fff || ctx->dst.y > 0x3fff || - ctx->dst_bits >= ctx->vram_end - bypp || - ctx->dst_bits + ctx->dst.x * bypp + (ctx->dst.y + ctx->dst.height) * - ctx->dst_stride >= ctx->vram_end - bypp) { + ctx->dst_bits >= vram_end - bypp || ctx->dst_bits + ctx->dst.x * bypp + + (ctx->dst.y + ctx->dst.height) * ctx->dst_stride >= vram_end - bypp) { qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n"); return false; } @@ -XXX,XX +XXX,XX @@ static bool ati_2d_do_blt(ATI2DCtx *ctx, uint8_t use_pixman) } if (!ctx->host_data_active && (vis_src.x > 0x3fff || vis_src.y > 0x3fff || - ctx->src_bits >= ctx->vram_end - bypp || + ctx->src_bits >= vram_end - bypp || ctx->src_bits + vis_src.x * bypp + (vis_src.y + vis_dst.height) * - ctx->src_stride >= ctx->vram_end - bypp)) { + ctx->src_stride >= vram_end - bypp)) { qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n"); return false; } @@ -XXX,XX +XXX,XX @@ static bool ati_2d_do_blt(ATI2DCtx *ctx, uint8_t use_pixman) case ROP3_BLACKNESS: case ROP3_WHITENESS: { + const uint8_t *palette = ctx->vga->palette; uint32_t filler = 0; if (ctx->bpp == 24) { @@ -XXX,XX +XXX,XX @@ static bool ati_2d_do_blt(ATI2DCtx *ctx, uint8_t use_pixman) filler = make_filler(ctx->bpp, ctx->frgd_clr); break; case ROP3_BLACKNESS: - filler = 0xffUL << 24 | rgb_to_pixel32(ctx->palette[0], - ctx->palette[1], - ctx->palette[2]); + filler = 0xffUL << 24 | rgb_to_pixel32(palette[0], palette[1], + palette[2]); break; case ROP3_WHITENESS: - filler = 0xffUL << 24 | rgb_to_pixel32(ctx->palette[3], - ctx->palette[4], - ctx->palette[5]); + filler = 0xffUL << 24 | rgb_to_pixel32(palette[3], palette[4], + palette[5]); break; } DPRINTF("pixman_fill(%p, %ld, %d, %d, %d, %d, %d, %x)\n", @@ -XXX,XX +XXX,XX @@ void ati_2d_blt(ATIVGAState *s) } setup_2d_blt_ctx(s, &ctx); if (ati_2d_do_blt(&ctx, s->use_pixman)) { - ati_set_dirty(&s->vga, &ctx); + ati_set_dirty(&ctx); } } @@ -XXX,XX +XXX,XX @@ bool ati_host_data_flush(ATIVGAState *s) DPRINTF("blt %dpx span @ row: %d, col: %d to dst (%d,%d)\n", pix_in_scanline, row, col, chunk.dst.x, chunk.dst.y); if (ati_2d_do_blt(&chunk, s->use_pixman)) { - ati_set_dirty(&s->vga, &chunk); + ati_set_dirty(&chunk); } idx += pix_in_scanline; col += pix_in_scanline; -- 2.53.0
From: Evgeny Kolmakov <randomjack94dev@gmail.com> Replace manual qemu_mutex_(un)lock() calls with QEMU_LOCK_GUARD() to remove 'goto cleanup' code Signed-off-by: Evgeny Kolmakov <randomjack94dev@gmail.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Message-ID: <20260622073647.3721-1-randomjack94dev@gmail.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- util/filemonitor-inotify.c | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/util/filemonitor-inotify.c b/util/filemonitor-inotify.c index XXXXXXX..XXXXXXX 100644 --- a/util/filemonitor-inotify.c +++ b/util/filemonitor-inotify.c @@ -XXX,XX +XXX,XX @@ #include "qemu/osdep.h" #include "qemu/filemonitor.h" #include "qemu/main-loop.h" +#include "qemu/lockable.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "trace.h" @@ -XXX,XX +XXX,XX @@ static void qemu_file_monitor_watch(void *arg) int used = 0; int len; - qemu_mutex_lock(&mon->lock); + QEMU_LOCK_GUARD(&mon->lock); if (mon->fd == -1) { - qemu_mutex_unlock(&mon->lock); return; } @@ -XXX,XX +XXX,XX @@ static void qemu_file_monitor_watch(void *arg) if (errno != EAGAIN) { error_report("Failure monitoring inotify FD '%s'," "disabling events", strerror(errno)); - goto cleanup; } /* no more events right now */ - goto cleanup; + return; } /* Loop over all events in the buffer */ @@ -XXX,XX +XXX,XX @@ static void qemu_file_monitor_watch(void *arg) } } } - - cleanup: - qemu_mutex_unlock(&mon->lock); } @@ -XXX,XX +XXX,XX @@ qemu_file_monitor_add_watch(QFileMonitor *mon, { QFileMonitorDir *dir; QFileMonitorWatch watch; - int64_t ret = -1; - qemu_mutex_lock(&mon->lock); + QEMU_LOCK_GUARD(&mon->lock); dir = g_hash_table_lookup(mon->dirs, dirpath); if (!dir) { int rv = inotify_add_watch(mon->fd, dirpath, @@ -XXX,XX +XXX,XX @@ qemu_file_monitor_add_watch(QFileMonitor *mon, if (rv < 0) { error_setg_errno(errp, errno, "Unable to watch '%s'", dirpath); - goto cleanup; + return -1; } trace_qemu_file_monitor_enable_watch(mon, dirpath, rv); @@ -XXX,XX +XXX,XX @@ qemu_file_monitor_add_watch(QFileMonitor *mon, filename ? filename : "<none>", cb, opaque, watch.id); - ret = watch.id; - - cleanup: - qemu_mutex_unlock(&mon->lock); - return ret; + return watch.id; } @@ -XXX,XX +XXX,XX @@ void qemu_file_monitor_remove_watch(QFileMonitor *mon, QFileMonitorDir *dir; gsize i; - qemu_mutex_lock(&mon->lock); + QEMU_LOCK_GUARD(&mon->lock); trace_qemu_file_monitor_remove_watch(mon, dirpath, id); dir = g_hash_table_lookup(mon->dirs, dirpath); if (!dir) { - goto cleanup; + return; } for (i = 0; i < dir->watches->len; i++) { @@ -XXX,XX +XXX,XX @@ void qemu_file_monitor_remove_watch(QFileMonitor *mon, qemu_set_fd_handler(mon->fd, NULL, NULL, NULL); } } - - cleanup: - qemu_mutex_unlock(&mon->lock); } -- 2.53.0
From: yujun <yujun@kylinos.cn> pca955x_get_led() and pca955x_set_led() accept led indices equal to pin_count, but valid indices are 0..pin_count-1. For a 16-pin device, led16 passes the current check and then accesses an LS register past max_reg. Use the same >= pin_count bounds check as pca9554_set_pin() and the gpio input handler assert in this file. Fixes: a90d8f84674 ("misc/pca9552: Add qom set and get") Signed-off-by: yujun <yujun@kylinos.cn> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Glenn Miles <milesg@linux.ibm.com> Message-ID: <20260629074133.187549-1-yujun@kylinos.cn> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- hw/gpio/pca9552.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/gpio/pca9552.c b/hw/gpio/pca9552.c index XXXXXXX..XXXXXXX 100644 --- a/hw/gpio/pca9552.c +++ b/hw/gpio/pca9552.c @@ -XXX,XX +XXX,XX @@ static void pca955x_get_led(Object *obj, Visitor *v, const char *name, error_setg(errp, "%s: error reading %s", __func__, name); return; } - if (led < 0 || led > k->pin_count) { - error_setg(errp, "%s invalid led %s", __func__, name); + if (led < 0 || led >= k->pin_count) { + error_setg(errp, "%s: invalid led %s", __func__, name); return; } /* @@ -XXX,XX +XXX,XX @@ static void pca955x_set_led(Object *obj, Visitor *v, const char *name, error_setg(errp, "%s: error reading %s", __func__, name); return; } - if (led < 0 || led > k->pin_count) { - error_setg(errp, "%s invalid led %s", __func__, name); + if (led < 0 || led >= k->pin_count) { + error_setg(errp, "%s: invalid led %s", __func__, name); return; } -- 2.53.0
From: yujun <yujun@kylinos.cn> trace_ebpf_rss_set_data() passes its third and fourth arguments to the toeplitz-ptr and indirection-ptr fields defined in trace-events. ebpf_rss_set_all() passed indirections_table and toeplitz_key in the opposite order, so tracing mislabeled the two pointers. Match the argument order already used by trace_ebpf_rss_mmap(). Fixes: f5cae19d10 ("ebpf: improve trace event coverage to all key operations") Signed-off-by: yujun <yujun@kylinos.cn> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Message-ID: <20260629090116.266561-1-yujun@kylinos.cn> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- ebpf/ebpf_rss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ebpf/ebpf_rss.c b/ebpf/ebpf_rss.c index XXXXXXX..XXXXXXX 100644 --- a/ebpf/ebpf_rss.c +++ b/ebpf/ebpf_rss.c @@ -XXX,XX +XXX,XX @@ bool ebpf_rss_set_all(struct EBPFRSSContext *ctx, struct EBPFRSSConfig *config, ebpf_rss_set_toepliz_key(ctx, toeplitz_key); - trace_ebpf_rss_set_data(ctx, config, indirections_table, toeplitz_key); + trace_ebpf_rss_set_data(ctx, config, toeplitz_key, indirections_table); return true; } -- 2.53.0
From: yujun <yujun@kylinos.cn> qcrypto_get_x509_cert_fingerprint() reports gnutls_strerror(ret) when gnutls_x509_crt_init() fails, but ret is still the initial value -1. Store the gnutls return code before formatting the error, matching other gnutls call sites in the tree. Fixes: 2183ab6251 ("crypto/x509-utils: Check for error from gnutls_x509_crt_init()") Signed-off-by: yujun <yujun@kylinos.cn> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Message-ID: <20260629111026.281185-1-yujun@kylinos.cn> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- crypto/x509-utils.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crypto/x509-utils.c b/crypto/x509-utils.c index XXXXXXX..XXXXXXX 100644 --- a/crypto/x509-utils.c +++ b/crypto/x509-utils.c @@ -XXX,XX +XXX,XX @@ int qcrypto_get_x509_cert_fingerprint(uint8_t *cert, size_t size, return -1; } - if (gnutls_x509_crt_init(&crt) < 0) { + ret = gnutls_x509_crt_init(&crt); + if (ret < 0) { error_setg(errp, "Unable to initialize certificate: %s", gnutls_strerror(ret)); return -1; -- 2.53.0
From: Peter Maydell <peter.maydell@linaro.org> The i8257 DMA controller has a "verify" mode, which the datasheet describes like this: > DMA verify, which does not actually involve the transfer of data. > When an 8257 channel is in the DMA verify mode, it will respond the > same as described for transfer operations, except that no memory or > I/O read/write control signals will be generated. When an 8257 > channel is in the DMA verify mode, it will respond the same as > described for transfer operations, except that no memory or I/O read > /write control signals will be generated, thus preventing the > transfer of data. The 8257, however, will gain control of the system > bus and will acknowledge the peripheral's DMA request for each DMA > cycle. The perihperal can use these acknowledge signals to enable an > internal access of each byte of a data block in order to execute some > verification procedure, such as the accumulation of a CRC check word. In practice, for QEMU's purposes the only real user of this is the floppy controller, which can be made to perform a "read data from floppy disk and check the checksum" by telling the fdc to do a read and the DMA controller to do a verify. This causes the fdc to do all the usual read actions including the checksum, but the data is never written to memory. However, it is possible for a guest doing something silly to program the DMA controller to do a verify operation for a device that wants to read from memory. Currently we simply return early from i8257_dma_read_memory() without writing to the buffer. None of the callers (the GUS, sb16 and cs4231a sound cards, plus the fdc) expect this, so they will take the uninitialized data as if it were from the guest. This can cause us to leak host data off the stack into the guest. Make i8257_dma_read_memory() fill the buffer with zeroes rather than leaving it untouched for a verify operation. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3487 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Message-ID: <20260629140128.1900095-1-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- hw/dma/i8257.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/hw/dma/i8257.c b/hw/dma/i8257.c index XXXXXXX..XXXXXXX 100644 --- a/hw/dma/i8257.c +++ b/hw/dma/i8257.c @@ -XXX,XX +XXX,XX @@ static int i8257_dma_read_memory(IsaDma *obj, int nchan, void *buf, int pos, hwaddr addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR]; if (i8257_is_verify_transfer(r)) { + /* + * If the device is expecting this verify operation then + * it won't care about the nonexistent data. But if it + * is expecting a real read (i.e. the guest has misprogrammed + * the DMA controller and the device) it's going to try to do + * something with the buffer contents. Give it zeroes. + * (It's not clear whether this is exactly what happens if + * you do this on real hardware. In practice no device QEMU + * emulates has a use for verify on a memory-read transfer, + * so we don't care beyond avoiding the guest being able to + * trigger the caller reading uninitialized data.) + */ + memset(buf, 0, len); return len; } -- 2.53.0
From: Sanjeeva Yerrapureddy <y.sanjeevreddy@gmail.com> When a guest transmits a short Ethernet frame, iov_size() returns the padded wire length including any bytes added to reach the Ethernet minimum frame size of 60 bytes. net_tx_pkt_rebuild_payload() uses this inflated size as payload_len. net_tx_pkt_update_ip_hdr_checksum() then overwrites the IPv4 Total Length field with payload_len + l3_hdr_len, inflating it by the padding. The receiver interprets Ethernet padding as IP payload, producing a malformed packet. Fix by removing the ip_len write from net_tx_pkt_update_ip_hdr_checksum() so it only recomputes the checksum, and moving the ip_len assignment into net_tx_pkt_update_ip_checksums() where it is only performed for TSO (where ip_len must be derived from payload_len since the guest sets ip_len=0 per Intel 82574 datasheet §7.3.4 for super-packets the host will segment). Both e1000e and igb already call net_tx_pkt_update_ip_hdr_checksum() from their IXSM paths, so both are corrected by this single common- layer change. Signed-off-by: Sanjeeva Yerrapureddy <y.sanjeevreddy@gmail.com> Reivewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Message-ID: <20260629-net-tx-pkt-ip-length-padding-v5-1-16760e30252e@gmail.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- hw/net/net_tx_pkt.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hw/net/net_tx_pkt.c b/hw/net/net_tx_pkt.c index XXXXXXX..XXXXXXX 100644 --- a/hw/net/net_tx_pkt.c +++ b/hw/net/net_tx_pkt.c @@ -XXX,XX +XXX,XX @@ void net_tx_pkt_update_ip_hdr_checksum(struct NetTxPkt *pkt) uint16_t csum; assert(pkt); - pkt->l3_hdr.ip.ip_len = cpu_to_be16(pkt->payload_len + - pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len); - pkt->l3_hdr.ip.ip_sum = 0; csum = net_raw_checksum(pkt->l3_hdr.octets, pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len); @@ -XXX,XX +XXX,XX @@ void net_tx_pkt_update_ip_checksums(struct NetTxPkt *pkt) if (gso_type == VIRTIO_NET_HDR_GSO_TCPV4 || gso_type == VIRTIO_NET_HDR_GSO_UDP) { - /* Calculate IP header checksum */ + /* Set ip_len and calculate IP header checksum */ + pkt->l3_hdr.ip.ip_len = cpu_to_be16(pkt->payload_len + + pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len); net_tx_pkt_update_ip_hdr_checksum(pkt); /* Calculate IP pseudo header checksum */ -- 2.53.0
From: Peter Maydell <peter.maydell@linaro.org> Currently the mptsas reset function clears intr_status, but it doesn't reset the doorbell state machine. This means that the state machine and the interrupt state get out of sync, and the guest can trigger an assertion failure in mptsas_doorbell_read() where s->doorbell_state is still DOORBELL_READ but s->intr_status does not have MPI_HIS_DOORBELL_INTERRUPT set. Fix this by having reset also reset the doorbell state. Strictly speaking we don't need to also clear doorbell_reply_idx and doorbell_reply_size, because those are only read when in DOORBELL_READ state, and the code always sets them up before transitioning into that state. But it's less confusing to clear them out on reset. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/304 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Message-ID: <20260629185035.2138238-1-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- hw/scsi/mptsas.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c index XXXXXXX..XXXXXXX 100644 --- a/hw/scsi/mptsas.c +++ b/hw/scsi/mptsas.c @@ -XXX,XX +XXX,XX @@ static void mptsas_soft_reset(MPTSASState *s) s->intr_status = 0; s->intr_mask = save_mask; + s->doorbell_state = DOORBELL_NONE; + s->doorbell_reply_idx = 0; + s->doorbell_reply_size = 0; + s->reply_free_tail = 0; s->reply_free_head = 0; s->reply_post_tail = 0; -- 2.53.0
From: Peter Maydell <peter.maydell@linaro.org> The hyperv_find_cpu() function finds a CPU from a CPU index; this is basically a wrapper around qemu_get_cpu(). It is allowed to fail, in which case it returns NULL, which its caller handles. However, it includes an assertion check which accidentally assumes the CPU pointer is non-NULL. We could assert only if cs != NULL, but the assertion here is not doing anything interesting -- hyperv_vp_index() is a trivial wrapper returning cs->cpu_index, so this is effectively asserting that qemu_get_cpu() did what it claims to do, i.e. returned us the CPU matching the index we gave it. qemu_get_cpu() is a simple "iterate through list and find matching CPU" which is unlikely to be buggy, and we don't feel the need to sanity-check it in any of our other many uses of it. Drop the assertion entirely. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3568 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Acked-by: Maciej S. Szmigiero <maciej.szmigiero@oracle.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Message-ID: <20260630084855.2319838-1-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- hw/hyperv/hyperv.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c index XXXXXXX..XXXXXXX 100644 --- a/hw/hyperv/hyperv.c +++ b/hw/hyperv/hyperv.c @@ -XXX,XX +XXX,XX @@ struct HvSintRoute { static CPUState *hyperv_find_vcpu(uint32_t vp_index) { - CPUState *cs = qemu_get_cpu(vp_index); - assert(hyperv_vp_index(cs) == vp_index); - return cs; + return qemu_get_cpu(vp_index); } /* -- 2.53.0
From: Thomas Huth <thuth@redhat.com> qxl_render_cursor() maps the guest-provided QXLCursor object using the guest-controlled cursor->chunk.data_size. For a mono cursor, qxl_cursor() then validates the expected bitmap size against cursor->data_size, but it does not validate that the first chunk actually contains that many bytes. A guest could set cursor->data_size to the correct full mono cursor size while setting cursor->chunk.data_size to zero. In that case, cursor_set_mono() reads the AND/XOR masks starting at cursor->chunk.data. If the cursor object is placed at the end of the QXL RAM BAR, those reads cross the mapped RAM region and could crash the QEMU process (e.g. under ASan). Fix it by double-checking cursor->chunk.data_size for the correct size. This patch is based on the suggested changes by the reporter in the bug ticket. Reported-by: huntr bubble Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3646 Signed-off-by: Thomas Huth <thuth@redhat.com> Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260630101022.379057-1-thuth@redhat.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- hw/display/qxl-render.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index XXXXXXX..XXXXXXX 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -XXX,XX +XXX,XX @@ static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor, case SPICE_CURSOR_TYPE_MONO: /* Assume that the full cursor is available in a single chunk. */ size = 2 * cursor_get_mono_bpl(c) * c->height; - if (size != cursor->data_size) { - fprintf(stderr, "%s: bad monochrome cursor %ux%u with size %u\n", - __func__, c->width, c->height, cursor->data_size); + if (size != cursor->data_size || cursor->chunk.data_size < size) { + qxl_set_guest_bug(qxl, "%s: bad monochrome cursor %ux%u" + " data_size %u chunk_size %u", + __func__, c->width, c->height, + cursor->data_size, cursor->chunk.data_size); goto fail; } and_mask = cursor->chunk.data; -- 2.53.0
From: Dmitry Frolov <frolov@swemel.ru> Memory for version_dir, allocated by g_strdup, is not freed Found by Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Dmitry Frolov <frolov@swemel.ru> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260630110601.736019-1-frolov@swemel.ru> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- util/module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/module.c b/util/module.c index XXXXXXX..XXXXXXX 100644 --- a/util/module.c +++ b/util/module.c @@ -XXX,XX +XXX,XX @@ int module_load(const char *prefix, const char *name, Error **errp) { int rv = -1; #ifdef CONFIG_MODULE_UPGRADES - char *version_dir; + g_autofree char *version_dir = NULL; #endif const char *search_dir; char *dirs[5]; -- 2.53.0
From: Song Gao <gaosong@loongson.cn> Validate guest-controlled cpu_num before using it to index the cpu[] array or pass to async_run_on_cpu(). Without this check, a malicious guest can trigger a NULL pointer dereference in async_run_on_cpu() and an out-of-bounds array access in qemu_set_irq(), causing host crash. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3616 Fixes: 0d148eaf5a3e ("hw/loongarch: Implement dintc set irq") Reported-by: Thomas Huth <thuth@redhat.com> Signed-off-by: Song Gao <gaosong@loongson.cn> Reviewed-by: Thomas Huth <thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Message-ID: <20260701065454.1976188-1-gaosong@loongson.cn> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- hw/intc/loongarch_dintc.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/hw/intc/loongarch_dintc.c b/hw/intc/loongarch_dintc.c index XXXXXXX..XXXXXXX 100644 --- a/hw/intc/loongarch_dintc.c +++ b/hw/intc/loongarch_dintc.c @@ -XXX,XX +XXX,XX @@ #include "target/loongarch/cpu.h" #include "qemu/error-report.h" #include "system/hw_accel.h" +#include "qemu/log.h" /* msg addr field */ FIELD(MSG_ADDR, IRQ_NUM, 4, 8) @@ -XXX,XX +XXX,XX @@ static void loongarch_dintc_mem_write(void *opaque, hwaddr addr, CPUState *cs; cpu_num = FIELD_EX64(msg_addr, MSG_ADDR, CPU_NUM); + + /* Validate cpu_num against the configured number of CPUs */ + if (cpu_num >= s->num_cpu) { + qemu_log_mask(LOG_GUEST_ERROR, + "loongarch-dintc: invalid cpu number%d\n", cpu_num); + return; + } cs = cpu_by_arch_id(cpu_num); + if (!cs) { + qemu_log_mask(LOG_GUEST_ERROR, + "loongarch-dintc: no CPU for arch_id %d\n", cpu_num); + return; + } irq_num = FIELD_EX64(msg_addr, MSG_ADDR, IRQ_NUM); async_run_on_cpu(cs, do_set_vcpu_dintc_irq, -- 2.53.0
From: Bernhard Beschow <shentey@gmail.com> When wiring struct FlexcanState to the clock control module, it is currently necessary to reach into its private data. Moreover, when forgetting to wire the clock control module, QEMU will crash after the guest has already started. Fix both by letting struct FlexcanState expose a link property whose sanity is checked at realize time. Suggested-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Signed-off-by: Bernhard Beschow <shentey@gmail.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Acked-by: Pavel Pisa <pisa@fel.cvut.cz> Tested-by: Pavel Pisa <pisa@fel.cvut.cz> Message-ID: <20260702184038.178196-2-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- hw/arm/fsl-imx6.c | 3 ++- hw/net/can/flexcan.c | 10 ++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) { FSL_IMX6_CAN2_ADDR, FSL_IMX6_FLEXCAN2_IRQ }, }; - s->flexcan[i].ccm = IMX_CCM(&s->ccm); + object_property_set_link(OBJECT(&s->flexcan[i]), "clock-control-module", + OBJECT(&s->ccm), &error_abort); object_property_set_link(OBJECT(&s->flexcan[i]), "canbus", OBJECT(s->canbus[i]), &error_abort); diff --git a/hw/net/can/flexcan.c b/hw/net/can/flexcan.c index XXXXXXX..XXXXXXX 100644 --- a/hw/net/can/flexcan.c +++ b/hw/net/can/flexcan.c @@ -XXX,XX +XXX,XX @@ static uint32_t flexcan_get_bitrate(FlexcanState *s) uint32_t pe_freq, s_freq, bitrate; - assert(s->ccm); - /* s_freq: CAN clock from CCM divided by the prescaler */ pe_freq = imx_ccm_get_clock_frequency(s->ccm, CLK_CAN); s_freq = pe_freq / (1 + conf_presdiv); @@ -XXX,XX +XXX,XX @@ static void flexcan_realize(DeviceState *dev, Error **errp) } } + if (!s->ccm) { + error_setg(errp, "%s 'clock-control-module' link property not set", + dev->canonical_path); + return; + } + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); sysbus_init_irq(SYS_BUS_DEVICE(SYS_BUS_DEVICE(dev)), &s->irq); } @@ -XXX,XX +XXX,XX @@ static const VMStateDescription vmstate_can = { static const Property flexcan_properties[] = { DEFINE_PROP_LINK("canbus", FlexcanState, canbus, TYPE_CAN_BUS, CanBusState *), + DEFINE_PROP_LINK("clock-control-module", FlexcanState, ccm, TYPE_IMX_CCM, + IMXCCMState *), }; static void flexcan_class_init(ObjectClass *klass, const void *data) -- 2.53.0
From: Bernhard Beschow <shentey@gmail.com> Subclass TYPE_CAN_FLEXCAN, yielding TYPE_CAN_FLEXCAN2 and TYPE_CAN_FLEXCAN3. Since TYPE_CAN_FLEXCAN is now abstract, TYPE_FSL_IMX6 needs to use TYPE_CAN_FLEXCAN2. TYPE_CAN_FLEXCAN3 will be used in TYPE_FSL_IMX8MP. Signed-off-by: Bernhard Beschow <shentey@gmail.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Acked-by: Pavel Pisa <pisa@fel.cvut.cz> Tested-by: Pavel Pisa <pisa@fel.cvut.cz> Message-ID: <20260702184038.178196-3-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- include/hw/net/flexcan.h | 4 +- hw/arm/fsl-imx6.c | 2 +- hw/net/can/flexcan.c | 80 ++++++++++++++++++++++++++++++++-------- 3 files changed, 69 insertions(+), 17 deletions(-) diff --git a/include/hw/net/flexcan.h b/include/hw/net/flexcan.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/net/flexcan.h +++ b/include/hw/net/flexcan.h @@ -XXX,XX +XXX,XX @@ typedef struct FlexcanState { } FlexcanState; #define TYPE_CAN_FLEXCAN "flexcan" - OBJECT_DECLARE_SIMPLE_TYPE(FlexcanState, CAN_FLEXCAN); +#define TYPE_CAN_FLEXCAN2 "flexcan2" +#define TYPE_CAN_FLEXCAN3 "flexcan3" + #endif diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx6_init(Object *obj) } for (i = 0; i < FSL_IMX6_NUM_CANS; i++) { snprintf(name, NAME_SIZE, "flexcan%d", i + 1); - object_initialize_child(obj, name, &s->flexcan[i], TYPE_CAN_FLEXCAN); + object_initialize_child(obj, name, &s->flexcan[i], TYPE_CAN_FLEXCAN2); } for (i = 0; i < FSL_IMX6_NUM_WDTS; i++) { snprintf(name, NAME_SIZE, "wdt%d", i); diff --git a/hw/net/can/flexcan.c b/hw/net/can/flexcan.c index XXXXXXX..XXXXXXX 100644 --- a/hw/net/can/flexcan.c +++ b/hw/net/can/flexcan.c @@ -XXX,XX +XXX,XX @@ static bool flexcan_mem_accepts(void *opaque, hwaddr addr, return true; } -static const struct MemoryRegionOps flexcan_ops = { +static const struct MemoryRegionOps flexcan2_ops = { .read = flexcan_mem_read, .write = flexcan_mem_write, .endianness = DEVICE_LITTLE_ENDIAN, @@ -XXX,XX +XXX,XX @@ static const struct MemoryRegionOps flexcan_ops = { }, }; +static const struct MemoryRegionOps flexcan3_ops = { + .read = flexcan_mem_read, + .write = flexcan_mem_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + .unaligned = true, + .accepts = flexcan_mem_accepts + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false + }, +}; + static CanBusClientInfo flexcan_bus_client_info = { .can_receive = flexcan_can_receive, .receive = flexcan_receive, @@ -XXX,XX +XXX,XX @@ static int flexcan_connect_to_bus(FlexcanState *s, CanBusState *bus) return 0; } -static void flexcan_init(Object *obj) +static void flexcan2_init(Object *obj) { FlexcanState *s = CAN_FLEXCAN(obj); memory_region_init_io( - &s->iomem, obj, &flexcan_ops, s, TYPE_CAN_FLEXCAN, + &s->iomem, obj, &flexcan2_ops, s, TYPE_CAN_FLEXCAN2, offsetof(FlexcanRegs, _reserved6) ); } +static void flexcan3_init(Object *obj) +{ + FlexcanState *s = CAN_FLEXCAN(obj); + + memory_region_init_io( + &s->iomem, obj, &flexcan3_ops, s, TYPE_CAN_FLEXCAN3, + sizeof(FlexcanRegs) + ); +} + static void flexcan_realize(DeviceState *dev, Error **errp) { FlexcanState *s = CAN_FLEXCAN(dev); @@ -XXX,XX +XXX,XX @@ static void flexcan_class_init(ObjectClass *klass, const void *data) dc->realize = flexcan_realize; device_class_set_props(dc, flexcan_properties); dc->vmsd = &vmstate_can; - dc->desc = "i.MX FLEXCAN Controller"; } -static const TypeInfo flexcan_info = { - .name = TYPE_CAN_FLEXCAN, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(FlexcanState), - .class_init = flexcan_class_init, - .instance_init = flexcan_init, +static void flexcan2_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "i.MX FlexCAN 2 Controller"; +} + +static void flexcan3_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "i.MX FlexCAN 3 Controller"; +} + +static const TypeInfo flexcan_types[] = { + { + .name = TYPE_CAN_FLEXCAN, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(FlexcanState), + .class_init = flexcan_class_init, + .abstract = true, + }, + { + .name = TYPE_CAN_FLEXCAN2, + .parent = TYPE_CAN_FLEXCAN, + .class_init = flexcan2_class_init, + .instance_init = flexcan2_init, + }, + { + .name = TYPE_CAN_FLEXCAN3, + .parent = TYPE_CAN_FLEXCAN, + .class_init = flexcan3_class_init, + .instance_init = flexcan3_init, + }, }; -static void can_register_types(void) -{ - type_register_static(&flexcan_info); -} -type_init(can_register_types) +DEFINE_TYPES(flexcan_types) -- 2.53.0
From: Bernhard Beschow <shentey@gmail.com> Open code the DEFINE_MACHINE_AARCH64 macro in preparation of creating FslImx8mpEvkState class, needed for FlexCAN emulation. Signed-off-by: Bernhard Beschow <shentey@gmail.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Acked-by: Pavel Pisa <pisa@fel.cvut.cz> Tested-by: Pavel Pisa <pisa@fel.cvut.cz> Message-ID: <20260702184038.178196-4-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- hw/arm/imx8mp-evk.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/hw/arm/imx8mp-evk.c b/hw/arm/imx8mp-evk.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/imx8mp-evk.c +++ b/hw/arm/imx8mp-evk.c @@ -XXX,XX +XXX,XX @@ static const char *imx8mp_evk_get_default_cpu_type(const MachineState *ms) return ARM_CPU_TYPE_NAME("cortex-a53"); } -static void imx8mp_evk_machine_init(MachineClass *mc) +static void imx8mp_evk_machine_class_init(ObjectClass *oc, const void *data) { + MachineClass *mc = MACHINE_CLASS(oc); + mc->desc = "NXP i.MX 8M Plus EVK Board"; mc->init = imx8mp_evk_init; mc->default_cpus = 4; @@ -XXX,XX +XXX,XX @@ static void imx8mp_evk_machine_init(MachineClass *mc) mc->get_default_cpu_type = imx8mp_evk_get_default_cpu_type; } -DEFINE_MACHINE_AARCH64("imx8mp-evk", imx8mp_evk_machine_init) +static const TypeInfo imx8mp_evk_machine_types[] = { + { + .name = MACHINE_TYPE_NAME("imx8mp-evk"), + .parent = TYPE_MACHINE, + .class_init = imx8mp_evk_machine_class_init, + .interfaces = aarch64_machine_interfaces, + }, +}; + +DEFINE_TYPES(imx8mp_evk_machine_types) -- 2.53.0
From: Bernhard Beschow <shentey@gmail.com> Introduce class FslImx8mpEvkState which will be needed for holding CanBusState attributes and associated properties. Signed-off-by: Bernhard Beschow <shentey@gmail.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Acked-by: Pavel Pisa <pisa@fel.cvut.cz> Tested-by: Pavel Pisa <pisa@fel.cvut.cz> Message-ID: <20260702184038.178196-5-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- hw/arm/imx8mp-evk.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/hw/arm/imx8mp-evk.c b/hw/arm/imx8mp-evk.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/imx8mp-evk.c +++ b/hw/arm/imx8mp-evk.c @@ -XXX,XX +XXX,XX @@ #include "qapi/error.h" #include <libfdt.h> +#define TYPE_IMX8MPEVK_MACHINE MACHINE_TYPE_NAME("imx8mp-evk") +OBJECT_DECLARE_SIMPLE_TYPE(FslImx8mpEvkState, IMX8MPEVK_MACHINE) + +struct FslImx8mpEvkState { + MachineState parent_obj; + + FslImx8mpState soc; + + struct arm_boot_info boot_info; +}; + static void imx8mp_evk_modify_dtb(const struct arm_boot_info *info, void *fdt) { int i, offset; @@ -XXX,XX +XXX,XX @@ static void imx8mp_evk_modify_dtb(const struct arm_boot_info *info, void *fdt) static void imx8mp_evk_init(MachineState *machine) { - static struct arm_boot_info boot_info; - FslImx8mpState *s; + FslImx8mpEvkState *s = IMX8MPEVK_MACHINE(machine); if (machine->ram_size > FSL_IMX8MP_RAM_SIZE_MAX) { error_report("RAM size " RAM_ADDR_FMT " above max supported (%08" PRIx64 ")", @@ -XXX,XX +XXX,XX @@ static void imx8mp_evk_init(MachineState *machine) exit(1); } - boot_info = (struct arm_boot_info) { + s->boot_info = (struct arm_boot_info) { .loader_start = FSL_IMX8MP_RAM_START, .board_id = -1, .ram_size = machine->ram_size, @@ -XXX,XX +XXX,XX @@ static void imx8mp_evk_init(MachineState *machine) .modify_dtb = imx8mp_evk_modify_dtb, }; - s = FSL_IMX8MP(object_new(TYPE_FSL_IMX8MP)); - object_property_add_child(OBJECT(machine), "soc", OBJECT(s)); - object_property_set_uint(OBJECT(s), "fec1-phy-num", 1, &error_fatal); - sysbus_realize_and_unref(SYS_BUS_DEVICE(s), &error_fatal); + object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_FSL_IMX8MP); + object_property_set_uint(OBJECT(&s->soc), "fec1-phy-num", 1, &error_fatal); + sysbus_realize_and_unref(SYS_BUS_DEVICE(&s->soc), &error_fatal); memory_region_add_subregion(get_system_memory(), FSL_IMX8MP_RAM_START, machine->ram); @@ -XXX,XX +XXX,XX @@ static void imx8mp_evk_init(MachineState *machine) } blk = blk_by_legacy_dinfo(di); - bus = qdev_get_child_bus(DEVICE(&s->usdhc[i]), "sd-bus"); + bus = qdev_get_child_bus(DEVICE(&s->soc.usdhc[i]), "sd-bus"); carddev = qdev_new(TYPE_SD_CARD); qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal); qdev_realize_and_unref(carddev, bus, &error_fatal); } if (!qtest_enabled()) { - arm_load_kernel(&s->cpu[0], machine, &boot_info); + arm_load_kernel(&s->soc.cpu[0], machine, &s->boot_info); } } @@ -XXX,XX +XXX,XX @@ static void imx8mp_evk_machine_class_init(ObjectClass *oc, const void *data) static const TypeInfo imx8mp_evk_machine_types[] = { { - .name = MACHINE_TYPE_NAME("imx8mp-evk"), + .name = TYPE_IMX8MPEVK_MACHINE, .parent = TYPE_MACHINE, .class_init = imx8mp_evk_machine_class_init, + .instance_size = sizeof(FslImx8mpEvkState), .interfaces = aarch64_machine_interfaces, }, }; -- 2.53.0
From: Bernhard Beschow <shentey@gmail.com> Real hardware supports CAN FD which is missing in the emulation and is considered future work. Still, CAN communication already works under Linux. Signed-off-by: Bernhard Beschow <shentey@gmail.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Acked-by: Pavel Pisa <pisa@fel.cvut.cz> Tested-by: Pavel Pisa <pisa@fel.cvut.cz> Message-ID: <20260702184038.178196-6-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- docs/system/arm/imx8m.rst | 1 + include/hw/arm/fsl-imx8mp.h | 8 ++++++++ hw/arm/fsl-imx8mp.c | 38 +++++++++++++++++++++++++++++++++++++ hw/arm/imx8mp-evk.c | 21 ++++++++++++++++++++ hw/misc/imx8mp_ccm.c | 3 +++ hw/arm/Kconfig | 1 + 6 files changed, 72 insertions(+) diff --git a/docs/system/arm/imx8m.rst b/docs/system/arm/imx8m.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/system/arm/imx8m.rst +++ b/docs/system/arm/imx8m.rst @@ -XXX,XX +XXX,XX @@ following devices: * 1 Designware PCI Express Controller * 1 Ethernet Controller * 2 Designware USB 3 Controllers + * 2 FlexCAN 3 CAN Controllers (``imx8mp-evk`` only) * 5 GPIO Controllers * 6 I2C Controllers * 3 SPI Controllers diff --git a/include/hw/arm/fsl-imx8mp.h b/include/hw/arm/fsl-imx8mp.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mp.h +++ b/include/hw/arm/fsl-imx8mp.h @@ -XXX,XX +XXX,XX @@ #include "hw/misc/imx7_snvs.h" #include "hw/misc/imx8mp_analog.h" #include "hw/misc/imx8mp_ccm.h" +#include "hw/net/flexcan.h" #include "hw/net/imx_fec.h" #include "hw/core/or-irq.h" #include "hw/pci-host/designware.h" @@ -XXX,XX +XXX,XX @@ OBJECT_DECLARE_SIMPLE_TYPE(FslImx8mpState, FSL_IMX8MP) #define FSL_IMX8MP_RAM_SIZE_MAX (8 * GiB) enum FslImx8mpConfiguration { + FSL_IMX8MP_NUM_CANS = 2, FSL_IMX8MP_NUM_CPUS = 4, FSL_IMX8MP_NUM_ECSPIS = 3, FSL_IMX8MP_NUM_GPIOS = 5, @@ -XXX,XX +XXX,XX @@ struct FslImx8mpState { USBDWC3 usb[FSL_IMX8MP_NUM_USBS]; DesignwarePCIEHost pcie; FslImx8mPciePhyState pcie_phy; + FlexcanState flexcan[FSL_IMX8MP_NUM_CANS]; OrIRQState gpt5_gpt6_irq; MemoryRegion ocram; uint32_t phy_num; bool phy_connected; + + CanBusState *canbus[FSL_IMX8MP_NUM_CANS]; }; enum FslImx8mpMemoryRegions { @@ -XXX,XX +XXX,XX @@ enum FslImx8mpIrqs { FSL_IMX8MP_PCI_INTC_IRQ = 124, FSL_IMX8MP_PCI_INTD_IRQ = 123, FSL_IMX8MP_PCI_MSI_IRQ = 140, + + FSL_IMX8MP_FLEXCAN1_IRQ = 142, + FSL_IMX8MP_FLEXCAN2_IRQ = 144, }; #endif /* FSL_IMX8MP_H */ diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mp.c +++ b/hw/arm/fsl-imx8mp.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mp_init(Object *obj) object_initialize_child(obj, name, &s->wdt[i], TYPE_IMX2_WDT); } + for (i = 0; i < FSL_IMX8MP_NUM_CANS; i++) { + g_autofree char *name = g_strdup_printf("flexcan%d", i); + object_initialize_child(obj, name, &s->flexcan[i], TYPE_CAN_FLEXCAN3); + } + object_initialize_child(obj, "eth0", &s->enet, TYPE_IMX_ENET); object_initialize_child(obj, "pcie", &s->pcie, TYPE_DESIGNWARE_PCIE_HOST); @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie_phy), 0, fsl_imx8mp_memmap[FSL_IMX8MP_PCIE_PHY1].addr); + /* FlexCANs */ + for (i = 0; i < FSL_IMX8MP_NUM_CANS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } flexcan_table[FSL_IMX8MP_NUM_CANS] = { + { + fsl_imx8mp_memmap[FSL_IMX8MP_FLEXCAN1].addr, + FSL_IMX8MP_FLEXCAN1_IRQ + }, { + fsl_imx8mp_memmap[FSL_IMX8MP_FLEXCAN2].addr, + FSL_IMX8MP_FLEXCAN2_IRQ + }, + }; + + object_property_set_link(OBJECT(&s->flexcan[i]), "clock-control-module", + OBJECT(&s->ccm), &error_abort); + object_property_set_link(OBJECT(&s->flexcan[i]), "canbus", + OBJECT(s->canbus[i]), &error_abort); + + sysbus_realize(SYS_BUS_DEVICE(&s->flexcan[i]), &error_abort); + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->flexcan[i]), 0, + flexcan_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->flexcan[i]), 0, + qdev_get_gpio_in(gicdev, flexcan_table[i].irq)); + } + /* On-Chip RAM */ if (!memory_region_init_ram(&s->ocram, OBJECT(dev), "imx8mp.ocram", fsl_imx8mp_memmap[FSL_IMX8MP_OCRAM].size, @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) switch (i) { case FSL_IMX8MP_ANA_PLL: case FSL_IMX8MP_CCM: + case FSL_IMX8MP_FLEXCAN1 ... FSL_IMX8MP_FLEXCAN2: case FSL_IMX8MP_GIC_DIST: case FSL_IMX8MP_GIC_REDIST: case FSL_IMX8MP_GPIO1 ... FSL_IMX8MP_GPIO5: @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) static const Property fsl_imx8mp_properties[] = { DEFINE_PROP_UINT32("fec1-phy-num", FslImx8mpState, phy_num, 0), DEFINE_PROP_BOOL("fec1-phy-connected", FslImx8mpState, phy_connected, true), + DEFINE_PROP_LINK("canbus0", FslImx8mpState, canbus[0], TYPE_CAN_BUS, + CanBusState *), + DEFINE_PROP_LINK("canbus1", FslImx8mpState, canbus[1], TYPE_CAN_BUS, + CanBusState *), }; static void fsl_imx8mp_class_init(ObjectClass *oc, const void *data) diff --git a/hw/arm/imx8mp-evk.c b/hw/arm/imx8mp-evk.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/imx8mp-evk.c +++ b/hw/arm/imx8mp-evk.c @@ -XXX,XX +XXX,XX @@ struct FslImx8mpEvkState { MachineState parent_obj; FslImx8mpState soc; + CanBusState *canbus[FSL_IMX8MP_NUM_CANS]; struct arm_boot_info boot_info; }; @@ -XXX,XX +XXX,XX @@ static void imx8mp_evk_init(MachineState *machine) object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_FSL_IMX8MP); object_property_set_uint(OBJECT(&s->soc), "fec1-phy-num", 1, &error_fatal); + for (int i = 0; i < FSL_IMX8MP_NUM_CANS; i++) { + g_autofree char *bus_name = g_strdup_printf("canbus%d", i); + + object_property_set_link(OBJECT(&s->soc), bus_name, + OBJECT(s->canbus[i]), &error_fatal); + } sysbus_realize_and_unref(SYS_BUS_DEVICE(&s->soc), &error_fatal); memory_region_add_subregion(get_system_memory(), FSL_IMX8MP_RAM_START, @@ -XXX,XX +XXX,XX @@ static const char *imx8mp_evk_get_default_cpu_type(const MachineState *ms) return ARM_CPU_TYPE_NAME("cortex-a53"); } +static void imx8mp_evk_machine_init(Object *obj) +{ + FslImx8mpEvkState *s = IMX8MPEVK_MACHINE(obj); + + object_property_add_link(obj, "canbus0", TYPE_CAN_BUS, + (Object **)&s->canbus[0], + object_property_allow_set_link, 0); + + object_property_add_link(obj, "canbus1", TYPE_CAN_BUS, + (Object **)&s->canbus[1], + object_property_allow_set_link, 0); +} + static void imx8mp_evk_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -XXX,XX +XXX,XX @@ static const TypeInfo imx8mp_evk_machine_types[] = { .name = TYPE_IMX8MPEVK_MACHINE, .parent = TYPE_MACHINE, .class_init = imx8mp_evk_machine_class_init, + .instance_init = imx8mp_evk_machine_init, .instance_size = sizeof(FslImx8mpEvkState), .interfaces = aarch64_machine_interfaces, }, diff --git a/hw/misc/imx8mp_ccm.c b/hw/misc/imx8mp_ccm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/misc/imx8mp_ccm.c +++ b/hw/misc/imx8mp_ccm.c @@ -XXX,XX +XXX,XX @@ static uint32_t imx8mp_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) case CLK_HIGH: freq = CKIH_FREQ; break; + case CLK_CAN: + freq = 40000000; /* 40Mhz, taken from device tree */ + break; case CLK_IPG: case CLK_IPG_HIGH: /* diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX8MP imply I2C_DEVICES imply PCI_DEVICES select ARM_GIC + select CAN_FLEXCAN select FSL_IMX8MP_ANALOG select FSL_IMX8MP_CCM select IMX -- 2.53.0
From: Mark Cave-Ayland <mark.caveayland@nutanix.com> This is to allow it to be the target of a class alias property. Signed-off-by: Mark Cave-Ayland <mark.caveayland@nutanix.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Message-ID: <20260703090814.2993188-6-mark.caveayland@nutanix.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- hw/rtc/mc146818rtc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index XXXXXXX..XXXXXXX 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -XXX,XX +XXX,XX @@ static void rtc_realizefn(DeviceState *dev, Error **errp) memory_region_add_subregion(&s->io, 0, &s->coalesced_io); memory_region_add_coalescing(&s->coalesced_io, 0, 1); - object_property_add_tm(OBJECT(s), "date", rtc_get_date); - qdev_init_gpio_out(dev, &s->irq, 1); } @@ -XXX,XX +XXX,XX @@ static void rtc_class_initfn(ObjectClass *klass, const void *data) adevc->build_dev_aml = rtc_build_aml; device_class_set_props(dc, mc146818rtc_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + object_class_property_add_tm(klass, "date", rtc_get_date); } static const TypeInfo mc146818rtc_info = { -- 2.53.0
From: Mark Cave-Ayland <mark.caveayland@nutanix.com> Signed-off-by: Mark Cave-Ayland <mark.caveayland@nutanix.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Message-ID: <20260703135512.3213964-7-mark.caveayland@nutanix.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- include/hw/acpi/ich9.h | 2 ++ hw/acpi/ich9.c | 8 ++++++-- hw/isa/lpc_ich9.c | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/acpi/ich9.h +++ b/include/hw/acpi/ich9.h @@ -XXX,XX +XXX,XX @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, qemu_irq sci_irq); void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base); extern const VMStateDescription vmstate_ich9_pm; +void ich9_pm_reset_properties(ICH9LPCPMRegs *pm); + void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm); void ich9_pm_device_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index XXXXXXX..XXXXXXX 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -XXX,XX +XXX,XX @@ static void ich9_pm_set_keep_pci_slot_hpc(Object *obj, bool value, Error **errp) s->pm.keep_pci_slot_hpc = value; } -void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm) +void ich9_pm_reset_properties(ICH9LPCPMRegs *pm) { - static const uint32_t gpe0_len = ICH9_PMIO_GPE0_LEN; pm->acpi_memory_hotplug.is_enabled = true; pm->disable_s3 = 0; pm->disable_s4 = 0; @@ -XXX,XX +XXX,XX @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm) pm->acpi_pci_hotplug.use_acpi_hotplug_bridge = true; pm->keep_pci_slot_hpc = true; pm->enable_tco = true; +} + +void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm) +{ + static const uint32_t gpe0_len = ICH9_PMIO_GPE0_LEN; object_property_add_uint32_ptr(obj, ACPI_PM_PROP_PM_IO_BASE, &pm->pm_io_base, OBJ_PROP_FLAG_READ); diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index XXXXXXX..XXXXXXX 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -XXX,XX +XXX,XX @@ static void ich9_lpc_initfn(Object *obj) &lpc->smi_negotiated_features, OBJ_PROP_FLAG_READ); + ich9_pm_reset_properties(&lpc->pm); ich9_pm_add_properties(obj, &lpc->pm); } -- 2.53.0
From: Shameer Kolothum <skolothumtho@nvidia.com> The trace event receives dev_id before type, but its format string prints them in the wrong order. Correct the order. Fixes: f2d31df0d925 ("backends/iommufd: Introduce iommufd_backend_alloc_viommu") Reported-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Message-ID: <20260706103653.84243-1-skolothumtho@nvidia.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- backends/trace-events | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/trace-events b/backends/trace-events index XXXXXXX..XXXXXXX 100644 --- a/backends/trace-events +++ b/backends/trace-events @@ -XXX,XX +XXX,XX @@ iommufd_backend_free_id(int iommufd, uint32_t id, int ret) " iommufd=%d id=%d (% iommufd_backend_set_dirty(int iommufd, uint32_t hwpt_id, bool start, int ret) " iommufd=%d hwpt=%u enable=%d (%d)" iommufd_backend_get_dirty_bitmap(int iommufd, uint32_t hwpt_id, uint64_t iova, uint64_t size, uint64_t flags, uint64_t page_size, int ret) " iommufd=%d hwpt=%u iova=0x%"PRIx64" size=0x%"PRIx64" flags=0x%"PRIx64" page_size=0x%"PRIx64" (%d)" iommufd_backend_invalidate_cache(int iommufd, uint32_t id, uint32_t data_type, uint32_t entry_len, uint32_t entry_num, uint32_t done_num, uint64_t data_ptr, int ret) " iommufd=%d id=%u data_type=%u entry_len=%u entry_num=%u done_num=%u data_ptr=0x%"PRIx64" (%d)" -iommufd_backend_alloc_viommu(int iommufd, uint32_t dev_id, uint32_t type, uint32_t hwpt_id, uint64_t data_ptr, uint32_t data_len, uint32_t viommu_id, int ret) " iommufd=%d type=%u dev_id=%u hwpt_id=%u data_ptr=0x%"PRIx64" data_len=0x%x viommu_id=%u (%d)" +iommufd_backend_alloc_viommu(int iommufd, uint32_t dev_id, uint32_t type, uint32_t hwpt_id, uint64_t data_ptr, uint32_t data_len, uint32_t viommu_id, int ret) " iommufd=%d dev_id=%u type=%u hwpt_id=%u data_ptr=0x%"PRIx64" data_len=0x%x viommu_id=%u (%d)" iommufd_backend_alloc_vdev(int iommufd, uint32_t dev_id, uint32_t viommu_id, uint64_t virt_id, uint32_t vdev_id, int ret) " iommufd=%d dev_id=%u viommu_id=%u virt_id=0x%"PRIx64" vdev_id=%u (%d)" iommufd_viommu_alloc_eventq(int iommufd, uint32_t viommu_id, uint32_t type, uint32_t veventq_id, uint32_t veventq_fd, int ret) " iommufd=%d viommu_id=%u type=%u veventq_id=%u veventq_fd=%u (%d)" iommufd_backend_alloc_hw_queue(int iommufd, uint32_t viommu_id, uint32_t queue_type, uint32_t index, uint64_t addr, uint64_t size, uint32_t queue_id, int ret) " iommufd=%d viommu_id=%u queue_type=%u index=%u addr=0x%"PRIx64" size=0x%"PRIx64" queue_id=%u (%d)" -- 2.53.0
From: Bin Meng <bin.meng@processmission.com> The m25p80 model uses s->needed_bytes to track how many bytes a controller must send after an opcode before the flash model can enter the data phase. For address-bearing commands this includes the address bytes. For fast-read commands it also includes the dummy phase. The tricky part is that flash datasheets describe the dummy phase in clock cycles, while the QEMU SSI interface advances the flash model one transferred byte at a time. The dummy clock count therefore has to be converted to the number of SSI bytes that the controller will actually emit. Some controllers have drivers that push these dummy bytes into a FIFO. Other controllers are programmed with a dummy-cycle count and generate the clocks themselves. The flash model still has to use the same byte count that a FIFO-style controller or the Linux spi-mem layer would use, otherwise the model waits too long and drops the first data bytes. Let's fix the inconsistency from the flash side first. We start from an easy one, the Winbond flashes. Per the Windbond W25Q256JV datasheet [1] instruction set table (chapter 8.1.2, 8.1.3, 8.1.4, 8.1.5), fix the wrong number of dummy bytes needed for fast read commands. [1] https://www.winbond.com/resource-files/w25q256jv%20spi%20revb%2009202016.pdf Fixes: fe8477052831 ("m25p80: Fix QIOR/DIOR handling for Winbond") Fixes: 3830c7a460b8 ("m25p80: Fix WINBOND fast read command handling") Fixes: cf6f1efe0b57 ("m25p80: Fast read commands family changes") Signed-off-by: Bin Meng <bin.meng@processmission.com> Tested-by: Cédric Le Goater <clg@redhat.com> Message-ID: <20260707083431.219671-2-bin.meng@processmission.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- hw/block/m25p80.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index XXXXXXX..XXXXXXX 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -XXX,XX +XXX,XX @@ static void decode_fast_read_cmd(Flash *s) s->needed_bytes += 1; break; case MAN_WINBOND: - s->needed_bytes += 8; + s->needed_bytes += 1; break; case MAN_NUMONYX: s->needed_bytes += numonyx_extract_cfg_num_dummies(s); @@ -XXX,XX +XXX,XX @@ static void decode_qio_read_cmd(Flash *s) switch (get_man(s)) { case MAN_WINBOND: s->needed_bytes += WINBOND_CONTINUOUS_READ_MODE_CMD_LEN; - s->needed_bytes += 4; + s->needed_bytes += 2; break; case MAN_SPANSION: s->needed_bytes += SPANSION_CONTINUOUS_READ_MODE_CMD_LEN; -- 2.53.0
From: Bin Meng <bin.meng@processmission.com> Numonyx/Micron flashes [1] do not use one fixed dummy-phase width for all fast-read commands. The volatile configuration register stores a number of dummy clock cycles, and QEMU must convert that value to the number of SSI bytes consumed by the flash model. Keep the existing default: 10 dummy clocks in Quad I/O mode and 8 dummy clocks otherwise. In Quad I/O and Dual I/O protocol modes, all command phases are transferred on 4 or 2 lines, so the dummy clock count still needs to be scaled by that bus width. Standard SPI, also called extended SPI in the Micron datasheet, is more subtle. Quad Output Fast Read (6Bh) and Dual Output Fast Read (3Bh) keep the opcode and address phases on DQ0; their dummy phase is just a clock gap before data is returned on four or two output lines. Do not scale the dummy count for those output-only commands. Only Quad I/O Fast Read (EBh) and Dual I/O Fast Read (BBh) transfer the address and dummy phases on the 4-bit or 2-bit bus, so keep scaling those commands. [1] https://docs.rs-online.com/cad7/0900766b8121bd3c.pdf Fixes: 23af26856606 ("hw/block/m25p80: Fix Numonyx fast read dummy cycle count") Signed-off-by: Bin Meng <bin.meng@processmission.com> Tested-by: Cédric Le Goater <clg@redhat.com> Message-ID: <20260707083431.219671-3-bin.meng@processmission.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- hw/block/m25p80.c | 55 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index XXXXXXX..XXXXXXX 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -XXX,XX +XXX,XX @@ static uint8_t numonyx_mode(Flash *s) } } -static uint8_t numonyx_extract_cfg_num_dummies(Flash *s) +static uint8_t numonyx_extract_cfg_dummy_bytes(Flash *s) { - uint8_t num_dummies; + uint8_t dummy_bits; uint8_t mode; - assert(get_man(s) == MAN_NUMONYX); mode = numonyx_mode(s); - num_dummies = extract32(s->volatile_cfg, 4, 4); + dummy_bits = extract32(s->volatile_cfg, 4, 4); - if (num_dummies == 0x0 || num_dummies == 0xf) { + /* + * The default nubmer of dummy cycles is only related to the SPI + * protocol mode. For QSPI it is 10, otherwise it is 8. + */ + if (dummy_bits == 0x0 || dummy_bits == 0xf) { + dummy_bits = (mode == MODE_QIO) ? 10 : 8; + } + + /* + * Convert the number of dummy cycles to bytes. + * + * In the Dual I/O and Quad I/O protocols, all command phases use 2 or 4 + * lines. In standard/extended SPI mode the phase width depends on the + * command sequence: output-only fast reads keep the dummy clocks on the + * single address line, while input/output fast reads use the same 2-line + * or 4-line phase as the address. + */ + + if (mode == MODE_QIO) { + dummy_bits *= 4; + } else if (mode == MODE_DIO) { + dummy_bits *= 2; + } else { switch (s->cmd_in_progress) { case QIOR: case QIOR4: - num_dummies = 10; + dummy_bits *= 4; break; - default: - num_dummies = (mode == MODE_QIO) ? 10 : 8; + case DIOR: + case DIOR4: + dummy_bits *= 2; break; - } + } } - return num_dummies; + /* + * Assert that the dummy bit count is byte-aligned + * as SSI core can only consume whole dummy bytes. + */ + assert(dummy_bits % 8 == 0); + + /* return the number of dummy bytes */ + return dummy_bits / 8; } static void decode_fast_read_cmd(Flash *s) @@ -XXX,XX +XXX,XX @@ static void decode_fast_read_cmd(Flash *s) s->needed_bytes += 1; break; case MAN_NUMONYX: - s->needed_bytes += numonyx_extract_cfg_num_dummies(s); + s->needed_bytes += numonyx_extract_cfg_dummy_bytes(s); break; case MAN_MACRONIX: if (extract32(s->volatile_cfg, 6, 2) == 1) { @@ -XXX,XX +XXX,XX @@ static void decode_dio_read_cmd(Flash *s) ); break; case MAN_NUMONYX: - s->needed_bytes += numonyx_extract_cfg_num_dummies(s); + s->needed_bytes += numonyx_extract_cfg_dummy_bytes(s); break; case MAN_MACRONIX: switch (extract32(s->volatile_cfg, 6, 2)) { @@ -XXX,XX +XXX,XX @@ static void decode_qio_read_cmd(Flash *s) ); break; case MAN_NUMONYX: - s->needed_bytes += numonyx_extract_cfg_num_dummies(s); + s->needed_bytes += numonyx_extract_cfg_dummy_bytes(s); break; case MAN_MACRONIX: switch (extract32(s->volatile_cfg, 6, 2)) { -- 2.53.0
From: Bin Meng <bin.meng@processmission.com> Macronix flashes expose DC[1:0] bits in the volatile configuration register [1]. These bits select the number of dummy clock cycles used by the fast-read command families. Convert the Macronix dummy-cycle settings through per-command-family tables and round up the non-byte-aligned cases that the byte-oriented SSI model cannot represent exactly. [1] https://www.macronix.com/Lists/Datasheet/Attachments/8657/MX66L51235F,%203V,%20512Mb,%20v1.1.pdf Fixes: cf6f1efe0b57 ("m25p80: Fast read commands family changes") Signed-off-by: Bin Meng <bin.meng@processmission.com> Tested-by: Cédric Le Goater <clg@redhat.com> Message-ID: <20260707083431.219671-4-bin.meng@processmission.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- hw/block/m25p80.c | 63 +++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index XXXXXXX..XXXXXXX 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -XXX,XX +XXX,XX @@ static uint8_t numonyx_extract_cfg_dummy_bytes(Flash *s) return dummy_bits / 8; } +static uint8_t macronix_extract_cfg_dummy_bytes(Flash *s, uint8_t bus_width) +{ + static const uint8_t dummy_cycles_fast[4] = { 8, 6, 8, 10 }; + static const uint8_t dummy_cycles_dio[4] = { 4, 6, 8, 10 }; + static const uint8_t dummy_cycles_qio[4] = { 6, 4, 8, 10 }; + const uint8_t *dummy_cycles = dummy_cycles_fast; + uint8_t dummy_bits; + + switch (s->cmd_in_progress) { + case DIOR: + case DIOR4: + dummy_cycles = dummy_cycles_dio; + break; + case QIOR: + case QIOR4: + dummy_cycles = dummy_cycles_qio; + break; + default: + break; + } + + dummy_bits = dummy_cycles[extract32(s->volatile_cfg, 6, 2)]; + dummy_bits *= bus_width; + + /* + * Assert that the dummy bit count is byte-aligned + * as SSI core can only consume whole dummy bytes. + */ + assert(dummy_bits % 8 == 0); + + return dummy_bits / 8; +} + static void decode_fast_read_cmd(Flash *s) { s->needed_bytes = get_addr_length(s); @@ -XXX,XX +XXX,XX @@ static void decode_fast_read_cmd(Flash *s) s->needed_bytes += numonyx_extract_cfg_dummy_bytes(s); break; case MAN_MACRONIX: - if (extract32(s->volatile_cfg, 6, 2) == 1) { - s->needed_bytes += 6; - } else { - s->needed_bytes += 8; - } + s->needed_bytes += macronix_extract_cfg_dummy_bytes(s, 1); break; case MAN_SPANSION: s->needed_bytes += extract32(s->spansion_cr2v, @@ -XXX,XX +XXX,XX @@ static void decode_dio_read_cmd(Flash *s) s->needed_bytes += numonyx_extract_cfg_dummy_bytes(s); break; case MAN_MACRONIX: - switch (extract32(s->volatile_cfg, 6, 2)) { - case 1: - s->needed_bytes += 6; - break; - case 2: - s->needed_bytes += 8; - break; - default: - s->needed_bytes += 4; - break; - } + s->needed_bytes += macronix_extract_cfg_dummy_bytes(s, 2); break; case MAN_ISSI: /* @@ -XXX,XX +XXX,XX @@ static void decode_qio_read_cmd(Flash *s) s->needed_bytes += numonyx_extract_cfg_dummy_bytes(s); break; case MAN_MACRONIX: - switch (extract32(s->volatile_cfg, 6, 2)) { - case 1: - s->needed_bytes += 4; - break; - case 2: - s->needed_bytes += 8; - break; - default: - s->needed_bytes += 6; - break; - } + s->needed_bytes += macronix_extract_cfg_dummy_bytes(s, 4); break; case MAN_ISSI: /* -- 2.53.0
From: Bin Meng <bin.meng@processmission.com> Change send_dummy_bits() to send_dummy_bytes() as the FIU register fields are programmed from spi_mem_op.dummy.nbytes, so they already describe byte transfers. Verified the changes by booting OpenBMC image on `gbs` machine all the way to the Linux login shell: $ qemu-system-arm -machine quanta-gbs-bmc -nographic \ -drive file=image.mtd,if=mtd,bus=0,unit=0,format=raw Fixes: b821242c7b3b ("hw/ssi: NPCM7xx Flash Interface Unit device model") Signed-off-by: Bin Meng <bin.meng@processmission.com> Tested-by: Cédric Le Goater <clg@redhat.com> Message-ID: <20260707083431.219671-6-bin.meng@processmission.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- hw/ssi/npcm7xx_fiu.c | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/hw/ssi/npcm7xx_fiu.c b/hw/ssi/npcm7xx_fiu.c index XXXXXXX..XXXXXXX 100644 --- a/hw/ssi/npcm7xx_fiu.c +++ b/hw/ssi/npcm7xx_fiu.c @@ -XXX,XX +XXX,XX @@ static uint64_t npcm7xx_fiu_flash_read(void *opaque, hwaddr addr, NPCM7xxFIUState *fiu = f->fiu; uint64_t value = 0; uint32_t drd_cfg; - int dummy_cycles; + int dummy_bytes; int i; if (fiu->active_cs != -1) { @@ -XXX,XX +XXX,XX @@ static uint64_t npcm7xx_fiu_flash_read(void *opaque, hwaddr addr, break; } - /* Flash chip model expects one transfer per dummy bit, not byte */ - dummy_cycles = - (FIU_DRD_CFG_DBW(drd_cfg) * 8) >> FIU_DRD_CFG_ACCTYPE(drd_cfg); - for (i = 0; i < dummy_cycles; i++) { + dummy_bytes = FIU_DRD_CFG_DBW(drd_cfg); + for (i = 0; i < dummy_bytes; i++) { ssi_transfer(fiu->spi, 0); } @@ -XXX,XX +XXX,XX @@ static void send_address(SSIBus *spi, unsigned int addsiz, uint32_t addr) } } -/* Send the number of dummy bits specified in the UMA config register. */ -static void send_dummy_bits(SSIBus *spi, uint32_t uma_cfg, uint32_t uma_cmd) +/* Send the number of dummy bytes specified in the UMA config register */ +static void send_dummy_bytes(SSIBus *spi, uint32_t uma_cfg) { - unsigned int bits_per_clock = 1U << FIU_UMA_CFG_DBPCK(uma_cfg); unsigned int i; for (i = 0; i < FIU_UMA_CFG_DBSIZ(uma_cfg); i++) { - /* Use bytes 0 and 1 first, then keep repeating byte 2 */ - unsigned int field = (i < 2) ? ((i + 1) * 8) : 24; - unsigned int j; - - for (j = 0; j < 8; j += bits_per_clock) { - ssi_transfer(spi, extract32(uma_cmd, field + j, bits_per_clock)); - } + ssi_transfer(spi, 0); } } @@ -XXX,XX +XXX,XX @@ static void npcm7xx_fiu_uma_transaction(NPCM7xxFIUState *s) ssi_transfer(s->spi, extract32(s->regs[reg], field, 8)); } - /* Send dummy bits, if present. */ - send_dummy_bits(s->spi, uma_cfg, s->regs[NPCM7XX_FIU_UMA_CMD]); + /* Send dummy bytes, if present */ + send_dummy_bytes(s->spi, uma_cfg); /* Read data, if present. */ for (i = 0; i < FIU_UMA_CFG_RDATSIZ(uma_cfg); i++) { -- 2.53.0
From: Bin Meng <bin.meng@processmission.com> The ZynqMP generic FIFO encodes dummy phases as a number of dummy cycles. QEMU's SSI bus transfers whole bytes, so the controller model must convert the programmed cycle count to the number of SSI byte transfers needed for the selected SPI, dual SPI or quad SPI mode. The legacy Xilinx QSPI snoop paths had the opposite problem after the m25p80 dummy handling was fixed. They still treated each dummy byte queued through the FIFO as a request to generate several SSI transfers based on the current link width. The flash model now consumes dummy phases as byte counts, so the manual FIFO path should forward one SSI transfer per dummy byte. Update the Xilinx QSPI dummy accounting consistently for the generic FIFO, manual FIFO and LQSPI direct-read paths. Also make the command table report the dummy byte counts consumed by m25p80 for dual and quad output reads, and account for the mode byte before LQSPI data reads begin. This matches the ZynqMP TRM (ug1085, v2.2 [1]) description of the generic FIFO dummy cycle entry and keeps the controller side aligned with the flash model's dummy byte ownership. The description of the generic command fifo register says: When [receive, transmit, data_xfer] = [0,0,1], the [immediate_data] field represents the number of dummy cycle sent on the SPI interface. [1] https://www.xilinx.com/support/documentation/user_guides/ug1085-zynq-ultrascale-trm.pdf table 24‐22, an example of Generic FIFO Contents for Quad I/O Read Command (EBh) Fixes: ef06ca3946e2 ("xilinx_spips: Add support for RX discard and RX drain") Fixes: c95997a39de6 ("xilinx_spips: Add support for the ZynqMP Generic QSPI") Signed-off-by: Bin Meng <bin.meng@processmission.com> Tested-by: Cédric Le Goater <clg@redhat.com> Message-ID: <20260707083431.219671-7-bin.meng@processmission.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- include/hw/ssi/xilinx_spips.h | 2 +- hw/ssi/xilinx_spips.c | 123 +++++++++++++++++++++++++--------- 2 files changed, 91 insertions(+), 34 deletions(-) diff --git a/include/hw/ssi/xilinx_spips.h b/include/hw/ssi/xilinx_spips.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/ssi/xilinx_spips.h +++ b/include/hw/ssi/xilinx_spips.h @@ -XXX,XX +XXX,XX @@ struct XilinxSPIPS { uint8_t num_busses; uint8_t snoop_state; - int cmd_dummies; + int cmd_dummy_bytes; uint8_t link_state; uint8_t link_state_next; uint8_t link_state_next_when; diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c index XXXXXXX..XXXXXXX 100644 --- a/hw/ssi/xilinx_spips.c +++ b/hw/ssi/xilinx_spips.c @@ -XXX,XX +XXX,XX @@ FIELD(GQSPI_GF_SNAPSHOT, EXPONENT, 9, 1) FIELD(GQSPI_GF_SNAPSHOT, DATA_XFER, 8, 1) FIELD(GQSPI_GF_SNAPSHOT, IMMEDIATE_DATA, 0, 8) +#define GQSPI_GF_MODE_SPI 1 +#define GQSPI_GF_MODE_DSPI 2 +#define GQSPI_GF_MODE_QSPI 3 + #define R_GQSPI_MOD_ID (0x1fc / 4) #define R_GQSPI_MOD_ID_RESET (0x10a0000) @@ -XXX,XX +XXX,XX @@ static void xilinx_spips_update_cs(XilinxSPIPS *s, int field) } if (!(field & ((1 << (s->num_cs * s->num_busses)) - 1))) { s->snoop_state = SNOOP_CHECKING; - s->cmd_dummies = 0; + s->cmd_dummy_bytes = 0; s->link_state = 1; s->link_state_next = 1; s->link_state_next_when = 0; @@ -XXX,XX +XXX,XX @@ static void xilinx_spips_reset(DeviceState *d) s->link_state_next = 1; s->link_state_next_when = 0; s->snoop_state = SNOOP_CHECKING; - s->cmd_dummies = 0; + s->cmd_dummy_bytes = 0; s->man_start_com = false; xilinx_spips_update_ixr(s); xilinx_spips_update_cs_lines(s); @@ -XXX,XX +XXX,XX @@ static void xlnx_zynqmp_qspips_flush_fifo_g(XlnxZynqMPQSPIPS *s) int i; if (!s->regs[R_GQSPI_DATA_STS]) { + uint32_t prev_gf_snapshot = s->regs[R_GQSPI_GF_SNAPSHOT]; uint8_t imm; s->regs[R_GQSPI_GF_SNAPSHOT] = fifo32_pop(&s->fifo_g); @@ -XXX,XX +XXX,XX @@ static void xlnx_zynqmp_qspips_flush_fifo_g(XlnxZynqMPQSPIPS *s) } s->regs[R_GQSPI_DATA_STS] = 1ul << imm; } else { - s->regs[R_GQSPI_DATA_STS] = imm; + /* + * When [receive, transmit, data_xfer] = [0,0,1], it represents + * the number of dummy cycle sent on the SPI interface. We need + * to convert the number of dummy cycles to bytes according to + * the SPI mode being used. + * + * Ref: ug1085 v2.2 (December 2020) table 24‐22, an example of + * Generic FIFO Contents for Quad I/O Read Command (EBh) + */ + if (!ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, TRANSMIT) && + !ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, RECIEVE)) { + uint8_t spi_mode = ARRAY_FIELD_EX32(s->regs, + GQSPI_GF_SNAPSHOT, + SPI_MODE); + /* + * Some ZynqMP GQSPI drivers, such as Linux, use the data + * bus width in the dummy GENFIFO entry only to configure + * the controller mode. The immediate value is already + * the number of dummy cycles for the dummy phase, which + * follows the address bus width. Reuse the previous TX + * phase mode to convert cycles to SSI bytes. + * + * This does not make the model Linux-only. U-Boot emits + * the dummy entry with op->dummy.buswidth, so the entry + * mode already matches the dummy phase. Its opcode and + * address phases are immediate entries, not DATA_XFER TX + * entries, so the override below is not taken for U-Boot. + */ + if (FIELD_EX32(prev_gf_snapshot, GQSPI_GF_SNAPSHOT, + DATA_XFER) && + FIELD_EX32(prev_gf_snapshot, GQSPI_GF_SNAPSHOT, + TRANSMIT) && + !FIELD_EX32(prev_gf_snapshot, GQSPI_GF_SNAPSHOT, + RECIEVE)) { + spi_mode = FIELD_EX32(prev_gf_snapshot, + GQSPI_GF_SNAPSHOT, SPI_MODE); + } + + if (spi_mode == GQSPI_GF_MODE_QSPI) { + s->regs[R_GQSPI_DATA_STS] = ROUND_UP(imm * 4, 8) / 8; + } else if (spi_mode == GQSPI_GF_MODE_DSPI) { + s->regs[R_GQSPI_DATA_STS] = ROUND_UP(imm * 2, 8) / 8; + } else if (spi_mode == GQSPI_GF_MODE_SPI) { + s->regs[R_GQSPI_DATA_STS] = ROUND_UP(imm * 1, 8) / 8; + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "Unknown SPI MODE: 0x%x ", spi_mode); + } + } else { + s->regs[R_GQSPI_DATA_STS] = imm; + } } } /* Zero length transfer check */ @@ -XXX,XX +XXX,XX @@ static void xlnx_zynqmp_qspips_flush_fifo_g(XlnxZynqMPQSPIPS *s) } } -static int xilinx_spips_num_dummies(XilinxQSPIPS *qs, uint8_t command) +static int xilinx_spips_num_dummy_bytes(XilinxQSPIPS *qs, uint8_t command) { if (!qs) { /* The SPI device is not a QSPI device */ @@ -XXX,XX +XXX,XX @@ static int xilinx_spips_num_dummies(XilinxQSPIPS *qs, uint8_t command) case QPP_4: return 0; case FAST_READ: - case DOR: - case QOR: case FAST_READ_4: + return 1; + case DOR: case DOR_4: + case QOR: case QOR_4: return 1; case DIOR: @@ -XXX,XX +XXX,XX @@ static void xilinx_spips_flush_txfifo(XilinxSPIPS *s) int i; uint8_t tx = 0; uint8_t tx_rx[MAX_NUM_BUSSES] = { 0 }; - uint8_t dummy_cycles = 0; uint8_t addr_length; if (fifo8_is_empty(&s->tx_fifo)) { @@ -XXX,XX +XXX,XX @@ static void xilinx_spips_flush_txfifo(XilinxSPIPS *s) tx_rx[i] = tx; } } else { - /* - * Extract a dummy byte and generate dummy cycles according to the - * link state - */ tx = fifo8_pop(&s->tx_fifo); - dummy_cycles = 8 / s->link_state; + for (i = 0; i < num_effective_busses(s); ++i) { + tx_rx[i] = tx; + } } for (i = 0; i < num_effective_busses(s); ++i) { int bus = num_effective_busses(s) - 1 - i; - if (dummy_cycles) { - int d; - for (d = 0; d < dummy_cycles; ++d) { - tx_rx[0] = ssi_transfer(s->spi[bus], (uint32_t)tx_rx[0]); - } - } else { - DB_PRINT_L(debug_level, "tx = %02x\n", tx_rx[i]); - tx_rx[i] = ssi_transfer(s->spi[bus], (uint32_t)tx_rx[i]); - DB_PRINT_L(debug_level, "rx = %02x\n", tx_rx[i]); - } + + DB_PRINT_L(debug_level, "tx = %02x\n", tx_rx[i]); + tx_rx[i] = ssi_transfer(s->spi[bus], (uint32_t)tx_rx[i]); + DB_PRINT_L(debug_level, "rx = %02x\n", tx_rx[i]); } if (s->regs[R_CMND] & R_CMND_RXFIFO_DRAIN) { @@ -XXX,XX +XXX,XX @@ static void xilinx_spips_flush_txfifo(XilinxSPIPS *s) switch (s->snoop_state) { case (SNOOP_CHECKING): /* Store the count of dummy bytes in the txfifo */ - s->cmd_dummies = xilinx_spips_num_dummies(q, tx); + s->cmd_dummy_bytes = xilinx_spips_num_dummy_bytes(q, tx); addr_length = get_addr_length(s, tx); - if (s->cmd_dummies < 0) { + if (s->cmd_dummy_bytes < 0) { s->snoop_state = SNOOP_NONE; } else { s->snoop_state = SNOOP_ADDR + addr_length - 1; @@ -XXX,XX +XXX,XX @@ static void xilinx_spips_flush_txfifo(XilinxSPIPS *s) case DOR: case DOR_4: s->link_state_next = 2; - s->link_state_next_when = addr_length + s->cmd_dummies; + s->link_state_next_when = addr_length + s->cmd_dummy_bytes; break; case QPP: case QPP_4: case QOR: case QOR_4: s->link_state_next = 4; - s->link_state_next_when = addr_length + s->cmd_dummies; + s->link_state_next_when = addr_length + s->cmd_dummy_bytes; break; case DIOR: case DIOR_4: @@ -XXX,XX +XXX,XX @@ static void xilinx_spips_flush_txfifo(XilinxSPIPS *s) /* * Address has been transmitted, transmit dummy cycles now if needed */ - if (s->cmd_dummies < 0) { + if (s->cmd_dummy_bytes < 0) { s->snoop_state = SNOOP_NONE; } else { - s->snoop_state = s->cmd_dummies; + s->snoop_state = s->cmd_dummy_bytes; } break; case (SNOOP_STRIPING): @@ -XXX,XX +XXX,XX @@ static void lqspi_load_cache(void *opaque, hwaddr addr) XilinxQSPIPS *q = opaque; XilinxSPIPS *s = opaque; int i; + int dummy_bytes; int flash_addr = ((addr & ~(LQSPI_CACHE_SIZE - 1)) / num_effective_busses(s)); int peripheral = flash_addr >> LQSPI_ADDRESS_BITS; int cache_entry = 0; uint32_t u_page_save = s->regs[R_LQSPI_STS] & ~LQSPI_CFG_U_PAGE; + uint8_t command; if (addr < q->lqspi_cached_addr || addr > q->lqspi_cached_addr + LQSPI_CACHE_SIZE - 4) { @@ -XXX,XX +XXX,XX @@ static void lqspi_load_cache(void *opaque, hwaddr addr) fifo8_reset(&s->rx_fifo); /* instruction */ + command = s->regs[R_LQSPI_CFG] & LQSPI_CFG_INST_CODE; DB_PRINT_L(0, "pushing read instruction: %02x\n", - (unsigned)(uint8_t)(s->regs[R_LQSPI_CFG] & - LQSPI_CFG_INST_CODE)); - fifo8_push(&s->tx_fifo, s->regs[R_LQSPI_CFG] & LQSPI_CFG_INST_CODE); + (unsigned)command); + fifo8_push(&s->tx_fifo, command); /* read address */ DB_PRINT_L(0, "pushing read address %06x\n", flash_addr); if (s->regs[R_LQSPI_CFG] & LQSPI_CFG_ADDR4) { @@ -XXX,XX +XXX,XX @@ static void lqspi_load_cache(void *opaque, hwaddr addr) fifo8_push(&s->tx_fifo, (uint8_t)(flash_addr >> 8)); fifo8_push(&s->tx_fifo, (uint8_t)flash_addr); /* mode bits */ + dummy_bytes = xilinx_spips_num_dummy_bytes(q, command); if (s->regs[R_LQSPI_CFG] & LQSPI_CFG_MODE_EN) { fifo8_push(&s->tx_fifo, extract32(s->regs[R_LQSPI_CFG], LQSPI_CFG_MODE_SHIFT, LQSPI_CFG_MODE_WIDTH)); + if (dummy_bytes > 0) { + dummy_bytes--; + } + } + if (dummy_bytes < 0) { + dummy_bytes = extract32(s->regs[R_LQSPI_CFG], + LQSPI_CFG_DUMMY_SHIFT, + LQSPI_CFG_DUMMY_WIDTH); } /* dummy bytes */ - for (i = 0; i < (extract32(s->regs[R_LQSPI_CFG], LQSPI_CFG_DUMMY_SHIFT, - LQSPI_CFG_DUMMY_WIDTH)); ++i) { + for (i = 0; i < dummy_bytes; ++i) { DB_PRINT_L(0, "pushing dummy byte\n"); fifo8_push(&s->tx_fifo, 0); } -- 2.53.0
From: Bin Meng <bin.meng@processmission.com> m25p80 now consumes fast-read dummy phases as byte counts. The ASPEED SMC direct-read path still treated the CEx dummy field as raw cycles and emitted field * 8 SSI transfers. Convert the ASPEED dummy field to SSI byte transfers using the selected direct-read data width. Fixes: ac2810defa9d ("aspeed/smc: handle dummy bytes when doing fast reads in command mode") Signed-off-by: Bin Meng <bin.meng@processmission.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Tested-by: Cédric Le Goater <clg@redhat.com> Message-ID: <20260707083431.219671-8-bin.meng@processmission.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- hw/ssi/aspeed_smc.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index XXXXXXX..XXXXXXX 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -XXX,XX +XXX,XX @@ static uint32_t aspeed_smc_check_segment_addr(const AspeedSMCFlash *fl, return addr; } -static int aspeed_smc_flash_dummies(const AspeedSMCFlash *fl) +static int aspeed_smc_flash_dummy_bytes(const AspeedSMCFlash *fl) { const AspeedSMCState *s = fl->controller; uint32_t r_ctrl0 = s->regs[s->r_ctrl0 + fl->cs]; uint32_t dummy_high = (r_ctrl0 >> CTRL_DUMMY_HIGH_SHIFT) & 0x1; uint32_t dummy_low = (r_ctrl0 >> CTRL_DUMMY_LOW_SHIFT) & 0x3; - uint32_t dummies = ((dummy_high << 2) | dummy_low) * 8; + uint32_t dummy_bytes = (dummy_high << 2) | dummy_low; - if (r_ctrl0 & CTRL_IO_DUAL_ADDR_DATA) { - dummies /= 2; + /* + * Scale the controller dummy field to SSI byte transfers using the + * direct-read dummy/address bus width. + */ + if ((r_ctrl0 & CTRL_IO_QPI) || + ((r_ctrl0 & CTRL_IO_QUAD_DATA) && + (r_ctrl0 & CTRL_IO_QUAD_ADDR_DATA))) { + dummy_bytes *= 4; + } else if ((r_ctrl0 & CTRL_IO_DUAL_DATA) && + (r_ctrl0 & CTRL_IO_DUAL_ADDR_DATA)) { + dummy_bytes *= 2; } - return dummies; + return dummy_bytes; } static void aspeed_smc_flash_setup(AspeedSMCFlash *fl, uint32_t addr) @@ -XXX,XX +XXX,XX @@ static void aspeed_smc_flash_setup(AspeedSMCFlash *fl, uint32_t addr) * settings, let's check for fast read mode. */ if (aspeed_smc_flash_mode(fl) == CTRL_FREADMODE) { - for (i = 0; i < aspeed_smc_flash_dummies(fl); i++) { + for (i = 0; i < aspeed_smc_flash_dummy_bytes(fl); i++) { ssi_transfer(fl->controller->spi, s->regs[R_DUMMY_DATA] & 0xff); } } -- 2.53.0
From: Bin Meng <bin.meng@processmission.com> This reverts commit 7faf6f1790dddf9f3acf6ddd95f7bbc1b4a755d0. The incorrect implementation of dummy cycles in m25p80 model is now corrected. Revert this commit. Signed-off-by: Bin Meng <bin.meng@processmission.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Tested-by: Cédric Le Goater <clg@redhat.com> Message-ID: <20260707083431.219671-9-bin.meng@processmission.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- hw/ssi/aspeed_smc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index XXXXXXX..XXXXXXX 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -XXX,XX +XXX,XX @@ static int aspeed_smc_num_dummies(uint8_t command) case FAST_READ: case DOR: case QOR: - case FAST_READ_4: case DOR_4: case QOR_4: return 1; case DIOR: + case FAST_READ_4: case DIOR_4: return 2; case QIOR: -- 2.53.0
From: Bin Meng <bin.meng@processmission.com> This reverts commit f95c4bffdc4c53b29f89762cab4adc5a43f95daf. The m25p80 model now accounts for fast-read dummy bytes in its command decoder. In ASPEED SMC model user mode, guest software already sends the complete byte stream, including any dummy bytes needed by the flash. Hence the model should just forward exactly the bytes supplied by the guest without the need of decoding guest-supplied flash op codes to inject extra dummy transfers. Signed-off-by: Bin Meng <bin.meng@processmission.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Tested-by: Cédric Le Goater <clg@redhat.com> Message-ID: <20260707083431.219671-10-bin.meng@processmission.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- include/hw/ssi/aspeed_smc.h | 2 - hw/ssi/aspeed_smc.c | 114 +----------------------------------- hw/ssi/trace-events | 1 - 3 files changed, 2 insertions(+), 115 deletions(-) diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/ssi/aspeed_smc.h +++ b/include/hw/ssi/aspeed_smc.h @@ -XXX,XX +XXX,XX @@ struct AspeedSMCState { AspeedSMCFlash flashes[ASPEED_SMC_CS_MAX]; - uint8_t snoop_index; - uint8_t snoop_dummies; bool unselect; }; diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index XXXXXXX..XXXXXXX 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -XXX,XX +XXX,XX @@ /* Flash opcodes. */ #define SPI_OP_READ 0x03 /* Read data bytes (low frequency) */ -#define SNOOP_OFF 0xFF -#define SNOOP_START 0x0 - /* * Default segments mapping addresses and size for each peripheral per * controller. These can be changed when board is initialized with the @@ -XXX,XX +XXX,XX @@ static MemTxResult aspeed_smc_flash_read(void *opaque, hwaddr addr, return MEMTX_OK; } -/* - * TODO (clg@kaod.org): stolen from xilinx_spips.c. Should move to a - * common include header. - */ -typedef enum { - READ = 0x3, READ_4 = 0x13, - FAST_READ = 0xb, FAST_READ_4 = 0x0c, - DOR = 0x3b, DOR_4 = 0x3c, - QOR = 0x6b, QOR_4 = 0x6c, - DIOR = 0xbb, DIOR_4 = 0xbc, - QIOR = 0xeb, QIOR_4 = 0xec, - - PP = 0x2, PP_4 = 0x12, - DPP = 0xa2, - QPP = 0x32, QPP_4 = 0x34, -} FlashCMD; - -static int aspeed_smc_num_dummies(uint8_t command) -{ - switch (command) { /* check for dummies */ - case READ: /* no dummy bytes/cycles */ - case PP: - case DPP: - case QPP: - case READ_4: - case PP_4: - case QPP_4: - return 0; - case FAST_READ: - case DOR: - case QOR: - case DOR_4: - case QOR_4: - return 1; - case DIOR: - case FAST_READ_4: - case DIOR_4: - return 2; - case QIOR: - case QIOR_4: - return 4; - default: - return -1; - } -} - -static bool aspeed_smc_do_snoop(AspeedSMCFlash *fl, uint64_t data, - unsigned size) -{ - AspeedSMCState *s = fl->controller; - uint8_t addr_width = aspeed_smc_flash_addr_width(fl); - - trace_aspeed_smc_do_snoop(fl->cs, s->snoop_index, s->snoop_dummies, - (uint8_t) data & 0xff); - - if (s->snoop_index == SNOOP_OFF) { - return false; /* Do nothing */ - - } else if (s->snoop_index == SNOOP_START) { - uint8_t cmd = data & 0xff; - int ndummies = aspeed_smc_num_dummies(cmd); - - /* - * No dummy cycles are expected with the current command. Turn - * off snooping and let the transfer proceed normally. - */ - if (ndummies <= 0) { - s->snoop_index = SNOOP_OFF; - return false; - } - - s->snoop_dummies = ndummies * 8; - - } else if (s->snoop_index >= addr_width + 1) { - - /* The SPI transfer has reached the dummy cycles sequence */ - for (; s->snoop_dummies; s->snoop_dummies--) { - ssi_transfer(s->spi, s->regs[R_DUMMY_DATA] & 0xff); - } - - /* If no more dummy cycles are expected, turn off snooping */ - if (!s->snoop_dummies) { - s->snoop_index = SNOOP_OFF; - } else { - s->snoop_index += size; - } - - /* - * Dummy cycles have been faked already. Ignore the current - * SPI transfer - */ - return true; - } - - s->snoop_index += size; - return false; -} - static MemTxResult aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data, unsigned size, MemTxAttrs attrs) { @@ -XXX,XX +XXX,XX @@ static MemTxResult aspeed_smc_flash_write(void *opaque, hwaddr addr, switch (aspeed_smc_flash_mode(fl)) { case CTRL_USERMODE: - if (aspeed_smc_do_snoop(fl, data, size)) { - break; - } - for (i = 0; i < size; i++) { ssi_transfer(s->spi, (data >> (8 * i)) & 0xff); } @@ -XXX,XX +XXX,XX @@ static void aspeed_smc_flash_update_ctrl(AspeedSMCFlash *fl, uint32_t value) s->regs[s->r_ctrl0 + fl->cs] = value; if (unselect != s->unselect) { - s->snoop_index = unselect ? SNOOP_OFF : SNOOP_START; aspeed_smc_flash_do_select(fl, unselect); } } @@ -XXX,XX +XXX,XX @@ static void aspeed_smc_reset_hold(Object *obj, ResetType type) aspeed_smc_flash_set_segment_region(s, i, asc->segment_to_reg(s, &asc->segments[i])); } - - s->snoop_index = SNOOP_OFF; - s->snoop_dummies = 0; } static MemTxResult aspeed_smc_read(void *opaque, hwaddr addr, uint64_t *data, @@ -XXX,XX +XXX,XX @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) static const VMStateDescription vmstate_aspeed_smc = { .name = "aspeed.smc", .version_id = 3, - .minimum_version_id = 2, + .minimum_version_id = 1, .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AspeedSMCState, ASPEED_SMC_R_MAX), - VMSTATE_UINT8(snoop_index, AspeedSMCState), - VMSTATE_UINT8(snoop_dummies, AspeedSMCState), + VMSTATE_UNUSED_V(2, 2), /* was snoop_index/snoop_dummies */ VMSTATE_BOOL_V(unselect, AspeedSMCState, 3), VMSTATE_END_OF_LIST() } diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events index XXXXXXX..XXXXXXX 100644 --- a/hw/ssi/trace-events +++ b/hw/ssi/trace-events @@ -XXX,XX +XXX,XX @@ aspeed_smc_flash_set_segment(int cs, uint64_t reg, uint64_t start, uint64_t end) "CS%d segreg=0x%"PRIx64" [ 0x%"PRIx64" - 0x%"PRIx64" ]" aspeed_smc_flash_read(int cs, uint64_t addr, uint32_t size, uint64_t data, int mode) "CS%d @0x%" PRIx64 " size %u: 0x%" PRIx64" mode:%d" -aspeed_smc_do_snoop(int cs, int index, int dummies, int data) "CS%d index:0x%x dummies:%d data:0x%x" aspeed_smc_flash_write(int cs, uint64_t addr, uint32_t size, uint64_t data, int mode) "CS%d @0x%" PRIx64 " size %u: 0x%" PRIx64" mode:%d" aspeed_smc_read(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64 aspeed_smc_dma_checksum(uint32_t addr, uint32_t data) "0x%08x: 0x%08x" -- 2.53.0
The following changes since commit 94826ec1370328375c3b6d1e80fdc94c8f46c348: Merge tag 'accel-20260706' of https://github.com/philmd/qemu into staging (2026-07-06 18:38:14 +0200) are available in the Git repository at: https://github.com/philmd/qemu.git tags/hw-misc-20260707 for you to fetch changes up to 01e53bea65945c483dbcfa02793f68463886fbe3: MAINTAINERS: update Chao Liu's email address (2026-07-07 17:11:52 +0200) ---------------------------------------------------------------- Misc HW patches - MAINTAINERS update - Fix in few trace event formats - A pair of improvements in util/ - FlexCAN3 to imx8mp-evk board - Various fixes in hw/ (EDU, ATI VGA, IDE AHCI, PCA9552, i8257 DMA, e1000e/igb, MPT SAS, Hyper-V, QXL, M25P80) v2: - Missing IDE AHCI Qtest - MSF2 M25P80 fix from Cédric - Update Chao Liu email ---------------------------------------------------------------- BALATON Zoltan (1): hw/display/ati: Include vga state in the blit context Bernhard Beschow (5): hw/net/can/flexcan: Wire clock control module via link property hw/net/can/flexcan: Subclass TYPE_CAN_FLEXCAN hw/arm/imx8mp-evk: Open code DEFINE_MACHINE_AARCH64 hw/arm/imx8mp-evk: Introduce FslImx8mpEvkState hw/arm: Add basic FlexCAN3 support to TYPE_FSL_IMX8MP and imx8mp-evk Bin Meng (9): hw/block: m25p80: Fix dummy byte handling for Winbond flash hw/block: m25p80: Fix dummy byte handling for Numonyx/Micron flash hw/block: m25p80: Fix dummy byte handling for Macronix flash hw/block: m25p80: Fix dummy byte handling for Spansion flash hw/ssi: npcm7xx_fiu: Correct the dummy cycle emulation logic hw/ssi: xilinx_spips: Fix dummy phase handling hw/ssi: aspeed_smc: Fix direct-read dummy bytes Revert "aspeed/smc: Fix number of dummy cycles for FAST_READ_4 command" Revert "aspeed/smc: snoop SPI transfers to fake dummy cycles" Chao Liu (1): MAINTAINERS: update Chao Liu's email address Cédric Le Goater (1): hw/arm/msf2-som: Fix spansion-cr2nv value for S25FL128S Denis V. Lunev (2): hw/ide/ahci: cancel in-flight buffered reads on command engine restart tests/qtest/ahci: test ATAPI read completing after engine restart Dmitry Frolov (1): utils/module: fix memleak in module_load() Evgeny Kolmakov (1): util/filemonitor-inotify: Use QEMU_LOCK_GUARD() Junjie Cao (2): hw/display/ati: reset host_data.next in write handler after flush hw/display/ati: guard against zero bpp in ati_host_data_flush Mark Cave-Ayland (2): hw/rtc/mc146818rtc: convert date from object prop to class prop hw/acpi/ich9: move initial property values into ich9_reset_properties() Peter Maydell (3): hw/dma/i8257: Return zeroes for read_memory in verify mode hw/scsi/mptsas: Reset doorbell state on reset hw/hyperv: Avoid crash if hyperv_find_cpu() passed invalid vp_index Sanjeeva Yerrapureddy (1): hw/net: fix e1000e/igb ip_len inflation by Ethernet minimum-frame padding Shameer Kolothum (1): backends/iommufd: Fix dev_id and type order in viommu trace Song Gao (1): hw/intc/loongarch_dintc: Fix OOB access in DINT MMIO write handler Thomas Huth (1): hw/display/qxl: Fix mono cursor validation that can read past a cursor chunk Torin Carey (1): hw/misc/edu: restrict dma access to dma buffer yujun (3): hw/gpio/pca9552: fix off-by-one in QOM led index validation ebpf: fix swapped toeplitz/indirection args in set_data trace crypto/x509-utils: fix gnutls error code in crt_init failure path MAINTAINERS | 6 +- docs/system/arm/imx8m.rst | 1 + include/hw/acpi/ich9.h | 2 + include/hw/arm/fsl-imx8mp.h | 8 ++ include/hw/net/flexcan.h | 4 +- include/hw/ssi/aspeed_smc.h | 2 - include/hw/ssi/xilinx_spips.h | 2 +- crypto/x509-utils.c | 3 +- ebpf/ebpf_rss.c | 2 +- hw/acpi/ich9.c | 8 +- hw/arm/fsl-imx6.c | 5 +- hw/arm/fsl-imx8mp.c | 38 +++++++++ hw/arm/imx8mp-evk.c | 64 +++++++++++--- hw/arm/msf2-som.c | 2 +- hw/block/m25p80.c | 154 ++++++++++++++++++++++------------ hw/display/ati.c | 2 + hw/display/ati_2d.c | 44 +++++----- hw/display/qxl-render.c | 8 +- hw/dma/i8257.c | 13 +++ hw/gpio/pca9552.c | 8 +- hw/hyperv/hyperv.c | 4 +- hw/ide/ahci.c | 3 + hw/intc/loongarch_dintc.c | 13 +++ hw/isa/lpc_ich9.c | 1 + hw/misc/edu.c | 24 +++--- hw/misc/imx8mp_ccm.c | 3 + hw/net/can/flexcan.c | 90 ++++++++++++++++---- hw/net/net_tx_pkt.c | 7 +- hw/rtc/mc146818rtc.c | 4 +- hw/scsi/mptsas.c | 4 + hw/ssi/aspeed_smc.c | 135 ++++------------------------- hw/ssi/npcm7xx_fiu.c | 25 ++---- hw/ssi/xilinx_spips.c | 123 +++++++++++++++++++-------- tests/qtest/ahci-test.c | 67 +++++++++++++++ util/filemonitor-inotify.c | 28 ++----- util/module.c | 2 +- backends/trace-events | 2 +- hw/arm/Kconfig | 1 + hw/ssi/trace-events | 1 - 39 files changed, 579 insertions(+), 334 deletions(-) -- 2.53.0
From: "Denis V. Lunev" <den@openvz.org> Add a regression test for the crash that occurs when a buffered ATAPI read completes after the command engine has been restarted. Issue an ATAPI READ_10 against a blkdebug-backed CD, suspend the backend read so it stays in flight, stop and restart the port's command engine (which re-maps the command list and clears cur_cmd), then release the read. The PIO and DMA reply paths fault in different AHCI helpers (ahci_pio_transfer() vs ahci_dma_rw_buf()), so cover both. The DMA variant is the reliable guard: on engine restart check_cmd() can re-arm cur_cmd before the old read completes, so the PIO variant does not fault in every build. The test only asserts that qemu survives a subsequent register access; if the blkdebug breakpoint ever failed to park the read it would pass without exercising the bug, as with the existing break/resume tests. Signed-off-by: Denis V. Lunev <den@openvz.org> Message-ID: <20260619112158.304782-3-den@openvz.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- tests/qtest/ahci-test.c | 67 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/tests/qtest/ahci-test.c b/tests/qtest/ahci-test.c index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/ahci-test.c +++ b/tests/qtest/ahci-test.c @@ -XXX,XX +XXX,XX @@ static void test_cdrom_pio_multi(void) ahci_test_cdrom_read10(3, false); } +/* + * Regression test: a buffered ATAPI read completing after a command + * engine restart must not dereference the cleared cur_cmd. Cover both + * PIO and DMA; the DMA variant is the reliable guard. + */ +static void test_atapi_engine_restart_in_flight(bool dma) +{ + AHCIQState *ahci; + AHCICommand *cmd; + unsigned char *tx; + char *iso; + int fd; + uint8_t port; + uint64_t buffer; + uint64_t iso_size = (uint64_t)ATAPI_SECTOR_SIZE * 2; + + fd = prepare_iso(iso_size, &tx, &iso); + + ahci = ahci_boot_and_enable("-drive if=none,id=drive0," + "file=blkdebug::%s,format=raw,readonly=on " + "-M q35 " + "-device ide-cd,drive=drive0 ", iso); + port = ahci_port_select(ahci); + + buffer = ahci_alloc(ahci, ATAPI_SECTOR_SIZE); + qtest_memset(ahci->parent->qts, buffer, 0x00, ATAPI_SECTOR_SIZE); + + /* Suspend the next backend read so the ATAPI read stays in flight. */ + g_free(qtest_hmp(ahci->parent->qts, + "qemu-io drive0 \"break read_aio rd\"")); + + cmd = ahci_atapi_command_create(CMD_ATAPI_READ_10, ATAPI_SECTOR_SIZE, + dma); + ahci_command_adjust(cmd, 0, buffer, ATAPI_SECTOR_SIZE, 0); + ahci_command_commit(ahci, cmd, port); + ahci_command_issue_async(ahci, cmd); + + /* Stop and restart the command engine to re-map the command list. */ + ahci_px_clr(ahci, port, AHCI_PX_CMD, AHCI_PX_CMD_ST); + ahci_px_set(ahci, port, AHCI_PX_CMD, AHCI_PX_CMD_ST); + + g_free(qtest_hmp(ahci->parent->qts, "qemu-io drive0 \"resume rd\"")); + + /* Round-trip through the device to confirm qemu is still alive. */ + ahci_px_rreg(ahci, port, AHCI_PX_TFD); + + ahci_command_free(cmd); + ahci_free(ahci, buffer); + g_free(tx); + ahci_shutdown(ahci); + remove_iso(fd, iso); +} + +static void test_atapi_engine_restart_pio(void) +{ + test_atapi_engine_restart_in_flight(false); +} + +static void test_atapi_engine_restart_dma(void) +{ + test_atapi_engine_restart_in_flight(true); +} + /* Regression test: Test that a READ_CD command with a BCL of 0 but a size of 0 * completes as a NOP instead of erroring out. */ static void test_atapi_bcl(void) @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv) qtest_add_func("/ahci/cdrom/pio/bcl", test_atapi_bcl); qtest_add_func("/ahci/cdrom/eject", test_atapi_tray); + qtest_add_func("/ahci/cdrom/engine_restart/pio", + test_atapi_engine_restart_pio); + qtest_add_func("/ahci/cdrom/engine_restart/dma", + test_atapi_engine_restart_dma); ret = g_test_run(); -- 2.53.0
From: Cédric Le Goater <clg@redhat.com> The emcraft-sf2 board set spansion-cr2nv to 1, which the old m25p80 code treated as a byte count. With the dummy cycle to byte conversion fix, CR2V=1 at SPI x1 is 1 bit, not byte-aligned, and triggers an assertion. Use the S25FL128S default of 0x8 (8 cycles = 1 byte at SPI x1), preserving the same runtime behavior. Signed-off-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Bin Meng <bin.meng@processmission.com> Tested-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Message-Id: <20d58663-a8d1-41ff-9348-cae4982c30f0@redhat.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- hw/arm/msf2-som.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/msf2-som.c b/hw/arm/msf2-som.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/msf2-som.c +++ b/hw/arm/msf2-som.c @@ -XXX,XX +XXX,XX @@ static void emcraft_sf2_s2s010_init(MachineState *machine) /* Attach SPI flash to SPI0 controller */ spi_bus = qdev_get_child_bus(dev, "spi0"); spi_flash = qdev_new("s25sl12801"); /* Spansion S25FL128SDPBHICO */ - qdev_prop_set_uint8(spi_flash, "spansion-cr2nv", 1); + qdev_prop_set_uint8(spi_flash, "spansion-cr2nv", 0x8); if (dinfo) { qdev_prop_set_drive_err(spi_flash, "drive", blk_by_legacy_dinfo(dinfo), &error_fatal); -- 2.53.0
From: Bin Meng <bin.meng@processmission.com> Spansion flashes expose the number of dummy clock cycles through CR2V register [1]. The value is a cycle count, not a byte count, so the m25p80 model has to convert it to the number of whole SSI transfer bytes consumed while collecting read command data. Add a helper that multiplies the CR2V dummy cycle count by the phase width and rounds up non-byte-aligned counts, matching the byte-oriented SSI model. The default eight-cycle configuration keeps the same byte counts as before. [1] https://www.infineon.com/assets/row/public/documents/10/49/infineon-s25fs128s-s25fs256s-1-datasheet-en.pdf Fixes: cf6f1efe0b57 ("m25p80: Fast read commands family changes") Signed-off-by: Bin Meng <bin.meng@processmission.com> Tested-by: Cédric Le Goater <clg@redhat.com> Message-ID: <20260707083431.219671-5-bin.meng@processmission.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- hw/block/m25p80.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index XXXXXXX..XXXXXXX 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -XXX,XX +XXX,XX @@ static uint8_t macronix_extract_cfg_dummy_bytes(Flash *s, uint8_t bus_width) return dummy_bits / 8; } +static uint8_t spansion_extract_cfg_dummy_bytes(Flash *s, uint8_t bus_width) +{ + uint8_t dummy_bits; + + dummy_bits = extract32(s->spansion_cr2v, SPANSION_DUMMY_CLK_POS, + SPANSION_DUMMY_CLK_LEN); + dummy_bits *= bus_width; + + /* + * Assert that the dummy bit count is byte-aligned + * as SSI core can only consume whole dummy bytes. + */ + assert(dummy_bits % 8 == 0); + + return dummy_bits / 8; +} + static void decode_fast_read_cmd(Flash *s) { s->needed_bytes = get_addr_length(s); @@ -XXX,XX +XXX,XX @@ static void decode_fast_read_cmd(Flash *s) s->needed_bytes += macronix_extract_cfg_dummy_bytes(s, 1); break; case MAN_SPANSION: - s->needed_bytes += extract32(s->spansion_cr2v, - SPANSION_DUMMY_CLK_POS, - SPANSION_DUMMY_CLK_LEN - ); + s->needed_bytes += spansion_extract_cfg_dummy_bytes(s, 1); break; case MAN_ISSI: /* @@ -XXX,XX +XXX,XX @@ static void decode_dio_read_cmd(Flash *s) break; case MAN_SPANSION: s->needed_bytes += SPANSION_CONTINUOUS_READ_MODE_CMD_LEN; - s->needed_bytes += extract32(s->spansion_cr2v, - SPANSION_DUMMY_CLK_POS, - SPANSION_DUMMY_CLK_LEN - ); + s->needed_bytes += spansion_extract_cfg_dummy_bytes(s, 2); break; case MAN_NUMONYX: s->needed_bytes += numonyx_extract_cfg_dummy_bytes(s); @@ -XXX,XX +XXX,XX @@ static void decode_qio_read_cmd(Flash *s) break; case MAN_SPANSION: s->needed_bytes += SPANSION_CONTINUOUS_READ_MODE_CMD_LEN; - s->needed_bytes += extract32(s->spansion_cr2v, - SPANSION_DUMMY_CLK_POS, - SPANSION_DUMMY_CLK_LEN - ); + s->needed_bytes += spansion_extract_cfg_dummy_bytes(s, 4); break; case MAN_NUMONYX: s->needed_bytes += numonyx_extract_cfg_dummy_bytes(s); -- 2.53.0
From: Chao Liu <chao.liu@processmission.com> I joined Process Mission over a month ago and have verified that sending patches from my new email address works as expected. Update my MAINTAINERS entries to use the new address for future upstream contributions. Signed-off-by: Chao Liu <chao.liu@processmission.com> Reviewed-by: Bin Meng <bin.meng@processmission.com> Message-ID: <20260707132701.71164-1-chao.liu@processmission.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- MAINTAINERS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ M: Alistair Francis <alistair.francis@wdc.com> R: Weiwei Li <liwei1518@gmail.com> R: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> R: Liu Zhiwei <zhiwei_liu@linux.alibaba.com> -R: Chao Liu <chao.liu.zevorn@gmail.com> +R: Chao Liu <chao.liu@processmission.com> L: qemu-riscv@nongnu.org S: Supported F: configs/targets/riscv* @@ -XXX,XX +XXX,XX @@ F: hw/riscv/xiangshan_kmh.c F: include/hw/riscv/xiangshan_kmh.h K230 Machines -M: Chao Liu <chao.liu.zevorn@gmail.com> +M: Chao Liu <chao.liu@processmission.com> L: qemu-riscv@nongnu.org S: Maintained F: docs/system/riscv/k230.rst @@ -XXX,XX +XXX,XX @@ F: hw/pci/pcie_doe.c F: docs/specs/spdm.rst Checkpatch -R: Chao Liu <chao.liu.zevorn@gmail.com> +R: Chao Liu <chao.liu@processmission.com> S: Odd Fixes F: scripts/checkpatch.pl -- 2.53.0