From 85379af1202264c46322218d8c2937aaf3daa3ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 1 May 2025 23:17:26 +0200 Subject: [PATCH 01/49] hw/timer/hpet: Remove HPETState::hpet_offset_saved field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The HPETState::hpet_offset_saved boolean was only set in the hw_compat_2_11[] array, via the 'hpet-offset-saved=false' property. We removed all machines using that array, let's remove that property and all the code around it. Message-ID: <20250501230129.2596-6-philmd@linaro.org> [thuth: Adapted the rust part to the current master branch] Signed-off-by: Thomas Huth Reviewed-by: Zhao Liu Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20260306093134.27789-1-thuth@redhat.com> --- hw/timer/hpet.c | 9 +-------- rust/hw/timer/hpet/src/device.rs | 9 +-------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index 0d7b8e0c7a..767093c431 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -75,7 +75,6 @@ struct HPETState { QemuMutex lock; MemoryRegion iomem; uint64_t hpet_offset; - bool hpet_offset_saved; QemuSeqLock state_version; qemu_irq irqs[HPET_NUM_IRQ_ROUTES]; uint32_t flags; @@ -272,11 +271,6 @@ static int hpet_post_load(void *opaque, int version_id) t->cmp64 = hpet_calculate_cmp64(t, s->hpet_counter, t->cmp); t->last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - NANOSECONDS_PER_SECOND; } - /* Recalculate the offset between the main counter and guest time */ - if (!s->hpet_offset_saved) { - s->hpet_offset = ticks_to_ns(s->hpet_counter) - - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - } return 0; } @@ -285,7 +279,7 @@ static bool hpet_offset_needed(void *opaque) { HPETState *s = opaque; - return hpet_enabled(s) && s->hpet_offset_saved; + return hpet_enabled(s); } static bool hpet_rtc_irq_level_needed(void *opaque) @@ -766,7 +760,6 @@ static const Property hpet_device_properties[] = { DEFINE_PROP_UINT8("timers", HPETState, num_timers, HPET_MIN_TIMERS), DEFINE_PROP_BIT("msi", HPETState, flags, HPET_MSI_SUPPORT, false), DEFINE_PROP_UINT32(HPET_INTCAP, HPETState, intcap, 0), - DEFINE_PROP_BOOL("hpet-offset-saved", HPETState, hpet_offset_saved, true), }; static void hpet_device_class_init(ObjectClass *klass, const void *data) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index ebf715d399..0a5c131819 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -627,8 +627,6 @@ pub struct HPETState { flags: u32, hpet_offset_migration: BqlCell, - #[property(rename = "hpet-offset-saved", default = true)] - hpet_offset_saved: bool, irqs: [InterruptSource; HPET_NUM_IRQ_ROUTES], rtc_irq_level: BqlCell, @@ -947,11 +945,6 @@ impl HPETState { tn_regs.last = CLOCK_VIRTUAL.get_ns() - NANOSECONDS_PER_SECOND; } - // Recalculate the offset between the main counter and guest time - if !self.hpet_offset_saved { - self.hpet_offset_migration - .set(ticks_to_ns(regs.counter) - CLOCK_VIRTUAL.get_ns()); - } regs.hpet_offset = self.hpet_offset_migration.get(); Ok(()) @@ -962,7 +955,7 @@ impl HPETState { } fn is_offset_needed(&self) -> bool { - self.regs.borrow().is_hpet_enabled() && self.hpet_offset_saved + self.regs.borrow().is_hpet_enabled() } fn validate_num_timers(&self, _version_id: u8) -> bool { From 7d2778dea32a4924a469f1cc5767042c8f5b4eea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 29 Apr 2025 16:45:39 +0200 Subject: [PATCH 02/49] hw/i386/pc: Remove deprecated pc-q35/pc-i440fx/xenfv 3.1 machines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These machines has been supported for a period of more than 6 years. According to our versioned machine support policy (see commit ce80c4fa6ff "docs: document special exception for machine type deprecation & removal") they can now be removed. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20260309145013.44327-2-philmd@linaro.org> --- hw/i386/pc_piix.c | 25 ------------------------- hw/i386/pc_q35.c | 14 -------------- 2 files changed, 39 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 29c8e99713..2b94c47244 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -655,19 +655,6 @@ static void pc_i440fx_machine_4_0_options(MachineClass *m) DEFINE_I440FX_MACHINE(4, 0); -static void pc_i440fx_machine_3_1_options(MachineClass *m) -{ - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - - pc_i440fx_machine_4_0_options(m); - m->smbus_no_migration_support = true; - pcmc->pvh_enabled = false; - compat_props_add(m->compat_props, hw_compat_3_1, hw_compat_3_1_len); - compat_props_add(m->compat_props, pc_compat_3_1, pc_compat_3_1_len); -} - -DEFINE_I440FX_MACHINE(3, 1); - #ifdef CONFIG_XEN static void xenfv_machine_4_2_options(MachineClass *m) { @@ -679,16 +666,4 @@ static void xenfv_machine_4_2_options(MachineClass *m) DEFINE_PC_MACHINE(xenfv_4_2, "xenfv-4.2", pc_xen_hvm_init, xenfv_machine_4_2_options); - -static void xenfv_machine_3_1_options(MachineClass *m) -{ - pc_i440fx_machine_3_1_options(m); - m->desc = "Xen Fully-virtualized PC"; - m->alias = "xenfv"; - m->max_cpus = HVM_MAX_VCPUS; - m->default_machine_opts = "accel=xen,suppress-vmdesc=on"; -} - -DEFINE_PC_MACHINE(xenfv, "xenfv-3.1", pc_xen_hvm_init, - xenfv_machine_3_1_options); #endif diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index f102da8e4a..52b8064b4d 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -600,17 +600,3 @@ static void pc_q35_machine_4_0_options(MachineClass *m) } DEFINE_Q35_MACHINE(4, 0); - -static void pc_q35_machine_3_1_options(MachineClass *m) -{ - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - - pc_q35_machine_4_0_options(m); - m->default_kernel_irqchip_split = false; - m->smbus_no_migration_support = true; - pcmc->pvh_enabled = false; - compat_props_add(m->compat_props, hw_compat_3_1, hw_compat_3_1_len); - compat_props_add(m->compat_props, pc_compat_3_1, pc_compat_3_1_len); -} - -DEFINE_Q35_MACHINE(3, 1); From 16d07e251ca8645f8509d404d177065e919a43e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 29 Apr 2025 16:48:11 +0200 Subject: [PATCH 03/49] hw/i386/pc: Remove PCMachineClass::pvh_enabled field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PCMachineClass::pvh_enabled boolean was only used by the pc-q35-3.1 and pc-i440fx-3.1 machines, which got removed. Remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20260307152635.83893-3-philmd@linaro.org> --- hw/i386/nitro_enclave.c | 2 +- hw/i386/pc.c | 6 ++---- hw/i386/x86-common.c | 3 +-- include/hw/i386/pc.h | 3 --- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/hw/i386/nitro_enclave.c b/hw/i386/nitro_enclave.c index 640b1d8c54..4cd18166ff 100644 --- a/hw/i386/nitro_enclave.c +++ b/hw/i386/nitro_enclave.c @@ -164,7 +164,7 @@ static void nitro_enclave_machine_initfn(Object *obj) } static void x86_load_eif(X86MachineState *x86ms, FWCfgState *fw_cfg, - int acpi_data_size, bool pvh_enabled) + int acpi_data_size) { Error *err = NULL; char *eif_kernel, *eif_initrd, *eif_cmdline; diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 052226baa5..3fc2f0a057 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -588,7 +588,6 @@ void xen_load_linux(PCMachineState *pcms) { int i; FWCfgState *fw_cfg; - PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); X86MachineState *x86ms = X86_MACHINE(pcms); assert(MACHINE(pcms)->kernel_filename != NULL); @@ -598,7 +597,7 @@ void xen_load_linux(PCMachineState *pcms) fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus); rom_set_fw(fw_cfg); - x86_load_linux(x86ms, fw_cfg, PC_FW_DATA, pcmc->pvh_enabled); + x86_load_linux(x86ms, fw_cfg, PC_FW_DATA, true); for (i = 0; i < nb_option_roms; i++) { assert(!strcmp(option_rom[i].name, "linuxboot_dma.bin") || !strcmp(option_rom[i].name, "pvh.bin") || @@ -932,7 +931,7 @@ void pc_memory_init(PCMachineState *pcms, } if (linux_boot) { - x86_load_linux(x86ms, fw_cfg, PC_FW_DATA, pcmc->pvh_enabled); + x86_load_linux(x86ms, fw_cfg, PC_FW_DATA, true); } for (i = 0; i < nb_option_roms; i++) { @@ -1673,7 +1672,6 @@ static void pc_machine_class_init(ObjectClass *oc, const void *data) pcmc->has_reserved_memory = true; pcmc->enforce_amd_1tb_hole = true; pcmc->isa_bios_alias = true; - pcmc->pvh_enabled = true; pcmc->kvmclock_create_always = true; x86mc->apic_xrupt_override = true; assert(!mc->get_hotplug_handler); diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index a420112666..db8d4e855f 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c @@ -704,8 +704,7 @@ void x86_load_linux(X86MachineState *x86ms, * saving the PVH entry point used by the x86/HVM direct boot ABI. * If load_elfboot() is successful, populate the fw_cfg info. */ - if (pvh_enabled && - load_elfboot(kernel_filename, kernel_size, + if (load_elfboot(kernel_filename, kernel_size, header, pvh_start_addr, fw_cfg)) { fclose(f); diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 5623660f5d..908de5e1ea 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -110,9 +110,6 @@ struct PCMachineClass { bool enforce_amd_1tb_hole; bool isa_bios_alias; - /* use PVH to load kernels that support this feature */ - bool pvh_enabled; - /* create kvmclock device even when KVM PV features are not exposed */ bool kvmclock_create_always; From b31f41d724cddfc9639bb574fa710082f9bc707e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 29 Apr 2025 16:50:30 +0200 Subject: [PATCH 04/49] hw/i386/x86: Remove @pvh_enabled argument in x86_load_linux() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit x86_load_linux()'s @pvh_enabled is now always %true: remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20260307152635.83893-4-philmd@linaro.org> --- hw/i386/microvm.c | 2 +- hw/i386/nitro_enclave.c | 2 +- hw/i386/pc.c | 4 ++-- hw/i386/x86-common.c | 3 +-- include/hw/i386/microvm.h | 2 +- include/hw/i386/x86.h | 3 +-- 6 files changed, 7 insertions(+), 9 deletions(-) diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index 7ff2051263..5a7889f21b 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -330,7 +330,7 @@ static void microvm_memory_init(MicrovmMachineState *mms) rom_set_fw(fw_cfg); if (machine->kernel_filename != NULL) { - mmc->x86_load_linux(x86ms, fw_cfg, 0, true); + mmc->x86_load_linux(x86ms, fw_cfg, 0); } if (mms->option_roms) { diff --git a/hw/i386/nitro_enclave.c b/hw/i386/nitro_enclave.c index 4cd18166ff..a29f0044d0 100644 --- a/hw/i386/nitro_enclave.c +++ b/hw/i386/nitro_enclave.c @@ -199,7 +199,7 @@ static void x86_load_eif(X86MachineState *x86ms, FWCfgState *fw_cfg, machine->kernel_cmdline = eif_cmdline; } - x86_load_linux(x86ms, fw_cfg, 0, true); + x86_load_linux(x86ms, fw_cfg, 0); unlink(machine->kernel_filename); unlink(machine->initrd_filename); diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 3fc2f0a057..00581dd8b4 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -597,7 +597,7 @@ void xen_load_linux(PCMachineState *pcms) fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus); rom_set_fw(fw_cfg); - x86_load_linux(x86ms, fw_cfg, PC_FW_DATA, true); + x86_load_linux(x86ms, fw_cfg, PC_FW_DATA); for (i = 0; i < nb_option_roms; i++) { assert(!strcmp(option_rom[i].name, "linuxboot_dma.bin") || !strcmp(option_rom[i].name, "pvh.bin") || @@ -931,7 +931,7 @@ void pc_memory_init(PCMachineState *pcms, } if (linux_boot) { - x86_load_linux(x86ms, fw_cfg, PC_FW_DATA, true); + x86_load_linux(x86ms, fw_cfg, PC_FW_DATA); } for (i = 0; i < nb_option_roms; i++) { diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index db8d4e855f..fde05fa7d7 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c @@ -642,8 +642,7 @@ static bool load_elfboot(const char *kernel_filename, void x86_load_linux(X86MachineState *x86ms, FWCfgState *fw_cfg, - int acpi_data_size, - bool pvh_enabled) + int acpi_data_size) { uint16_t protocol; int setup_size, kernel_size, cmdline_size; diff --git a/include/hw/i386/microvm.h b/include/hw/i386/microvm.h index 6b9a506527..184b7a8c09 100644 --- a/include/hw/i386/microvm.h +++ b/include/hw/i386/microvm.h @@ -79,7 +79,7 @@ struct MicrovmMachineClass { HotplugHandler *(*orig_hotplug_handler)(MachineState *machine, DeviceState *dev); void (*x86_load_linux)(X86MachineState *x86ms, FWCfgState *fw_cfg, - int acpi_data_size, bool pvh_enabled); + int acpi_data_size); }; struct MicrovmMachineState { diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index a85a5600ce..71fe6b5e12 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -129,8 +129,7 @@ void x86_bios_rom_reload(X86MachineState *x86ms); void x86_load_linux(X86MachineState *x86ms, FWCfgState *fw_cfg, - int acpi_data_size, - bool pvh_enabled); + int acpi_data_size); bool x86_machine_is_smm_enabled(const X86MachineState *x86ms); bool x86_machine_is_acpi_enabled(const X86MachineState *x86ms); From 7d1e9bcfe9f178a0cff3b38a05118accc3087f15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 29 Apr 2025 16:53:56 +0200 Subject: [PATCH 05/49] hw/core/boards: Remove MachineClass::smbus_no_migration_support field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MachineClass::smbus_no_migration_support boolean was only used by the pc-q35-3.1 and pc-i440fx-3.1 machines, which got removed. Remove it, along with pm_smbus_vmstate_needed(). Use the unconditional VMSTATE_BOOL() and VMSTATE_STRUCT() macros. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Acked-by: Corey Minyard Message-Id: <20260307152635.83893-5-philmd@linaro.org> --- hw/acpi/piix4.c | 8 +------- hw/i2c/pm_smbus.c | 7 ------- hw/i2c/smbus_eeprom.c | 4 +--- hw/i2c/smbus_ich9.c | 10 ++-------- include/hw/core/boards.h | 1 - include/hw/i2c/pm_smbus.h | 7 ------- 6 files changed, 4 insertions(+), 33 deletions(-) diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 138ac3d394..43860d1227 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -205,11 +205,6 @@ static const VMStateDescription vmstate_cpuhp_state = { } }; -static bool piix4_vmstate_need_smbus(void *opaque, int version_id) -{ - return pm_smbus_vmstate_needed(); -} - /* * This is a fudge to turn off the acpi_index field, * whose test was always broken on piix4 with 6.2 and older machine types. @@ -238,8 +233,7 @@ static const VMStateDescription vmstate_acpi = { VMSTATE_UINT16(ar.pm1.evt.en, PIIX4PMState), VMSTATE_UINT16(ar.pm1.cnt.cnt, PIIX4PMState), VMSTATE_STRUCT(apm, PIIX4PMState, 0, vmstate_apm, APMState), - VMSTATE_STRUCT_TEST(smb, PIIX4PMState, piix4_vmstate_need_smbus, 3, - pmsmb_vmstate, PMSMBus), + VMSTATE_STRUCT(smb, PIIX4PMState, 3, pmsmb_vmstate, PMSMBus), VMSTATE_TIMER_PTR(ar.tmr.timer, PIIX4PMState), VMSTATE_INT64(ar.tmr.overflow_time, PIIX4PMState), VMSTATE_STRUCT(ar.gpe, PIIX4PMState, 2, vmstate_gpe, ACPIGPE), diff --git a/hw/i2c/pm_smbus.c b/hw/i2c/pm_smbus.c index ead53d2724..5654d51638 100644 --- a/hw/i2c/pm_smbus.c +++ b/hw/i2c/pm_smbus.c @@ -443,13 +443,6 @@ static const MemoryRegionOps pm_smbus_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -bool pm_smbus_vmstate_needed(void) -{ - MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); - - return !mc->smbus_no_migration_support; -} - const VMStateDescription pmsmb_vmstate = { .name = "pmsmb", .version_id = 1, diff --git a/hw/i2c/smbus_eeprom.c b/hw/i2c/smbus_eeprom.c index d9ac556a0a..693d476220 100644 --- a/hw/i2c/smbus_eeprom.c +++ b/hw/i2c/smbus_eeprom.c @@ -88,11 +88,9 @@ static int eeprom_write_data(SMBusDevice *dev, uint8_t *buf, uint8_t len) static bool smbus_eeprom_vmstate_needed(void *opaque) { - MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); SMBusEEPROMDevice *eeprom = opaque; - return (eeprom->accessed || smbus_vmstate_needed(&eeprom->smbusdev)) && - !mc->smbus_no_migration_support; + return eeprom->accessed || smbus_vmstate_needed(&eeprom->smbusdev); } static const VMStateDescription vmstate_smbus_eeprom = { diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c index 956c9b59bb..51513e73fd 100644 --- a/hw/i2c/smbus_ich9.c +++ b/hw/i2c/smbus_ich9.c @@ -41,20 +41,14 @@ struct ICH9SMBState { PMSMBus smb; }; -static bool ich9_vmstate_need_smbus(void *opaque, int version_id) -{ - return pm_smbus_vmstate_needed(); -} - static const VMStateDescription vmstate_ich9_smbus = { .name = "ich9_smb", .version_id = 1, .minimum_version_id = 1, .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, ICH9SMBState), - VMSTATE_BOOL_TEST(irq_enabled, ICH9SMBState, ich9_vmstate_need_smbus), - VMSTATE_STRUCT_TEST(smb, ICH9SMBState, ich9_vmstate_need_smbus, 1, - pmsmb_vmstate, PMSMBus), + VMSTATE_BOOL(irq_enabled, ICH9SMBState), + VMSTATE_STRUCT(smb, ICH9SMBState, 1, pmsmb_vmstate, PMSMBus), VMSTATE_END_OF_LIST() } }; diff --git a/include/hw/core/boards.h b/include/hw/core/boards.h index 9ad2a7d5c9..e15dbcea83 100644 --- a/include/hw/core/boards.h +++ b/include/hw/core/boards.h @@ -314,7 +314,6 @@ struct MachineClass { bool auto_enable_numa_with_memhp; bool auto_enable_numa_with_memdev; bool ignore_boot_device_suffixes; - bool smbus_no_migration_support; bool nvdimm_supported; bool numa_mem_supported; bool auto_enable_numa; diff --git a/include/hw/i2c/pm_smbus.h b/include/hw/i2c/pm_smbus.h index dafe0df4f6..62bbd45e80 100644 --- a/include/hw/i2c/pm_smbus.h +++ b/include/hw/i2c/pm_smbus.h @@ -44,13 +44,6 @@ typedef struct PMSMBus { void pm_smbus_init(DeviceState *parent, PMSMBus *smb, bool force_aux_blk); -/* - * For backwards compatibility on migration, older versions don't have - * working migration for pm_smbus, this lets us ignore the migrations - * for older machine versions. - */ -bool pm_smbus_vmstate_needed(void); - extern const VMStateDescription pmsmb_vmstate; #endif /* PM_SMBUS_H */ From 14510958af72c820b0384ac6de82a062779bd570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 29 Apr 2025 16:55:39 +0200 Subject: [PATCH 06/49] hw/i386/pc: Remove pc_compat_3_1[] array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pc_compat_3_1[] array was only used by the pc-q35-3.1 and pc-i440fx-3.1 machines, which got removed. Remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20260307152635.83893-6-philmd@linaro.org> --- hw/i386/pc.c | 25 ------------------------- include/hw/i386/pc.h | 3 --- 2 files changed, 28 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 00581dd8b4..f33e3f7265 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -180,31 +180,6 @@ const size_t pc_compat_4_1_len = G_N_ELEMENTS(pc_compat_4_1); GlobalProperty pc_compat_4_0[] = {}; const size_t pc_compat_4_0_len = G_N_ELEMENTS(pc_compat_4_0); -GlobalProperty pc_compat_3_1[] = { - { "intel-iommu", "dma-drain", "off" }, - { "Opteron_G3" "-" TYPE_X86_CPU, "rdtscp", "off" }, - { "Opteron_G4" "-" TYPE_X86_CPU, "rdtscp", "off" }, - { "Opteron_G4" "-" TYPE_X86_CPU, "npt", "off" }, - { "Opteron_G4" "-" TYPE_X86_CPU, "nrip-save", "off" }, - { "Opteron_G5" "-" TYPE_X86_CPU, "rdtscp", "off" }, - { "Opteron_G5" "-" TYPE_X86_CPU, "npt", "off" }, - { "Opteron_G5" "-" TYPE_X86_CPU, "nrip-save", "off" }, - { "EPYC" "-" TYPE_X86_CPU, "npt", "off" }, - { "EPYC" "-" TYPE_X86_CPU, "nrip-save", "off" }, - { "EPYC-IBPB" "-" TYPE_X86_CPU, "npt", "off" }, - { "EPYC-IBPB" "-" TYPE_X86_CPU, "nrip-save", "off" }, - { "Skylake-Client" "-" TYPE_X86_CPU, "mpx", "on" }, - { "Skylake-Client-IBRS" "-" TYPE_X86_CPU, "mpx", "on" }, - { "Skylake-Server" "-" TYPE_X86_CPU, "mpx", "on" }, - { "Skylake-Server-IBRS" "-" TYPE_X86_CPU, "mpx", "on" }, - { "Cascadelake-Server" "-" TYPE_X86_CPU, "mpx", "on" }, - { "Icelake-Client" "-" TYPE_X86_CPU, "mpx", "on" }, - { "Icelake-Server" "-" TYPE_X86_CPU, "mpx", "on" }, - { "Cascadelake-Server" "-" TYPE_X86_CPU, "stepping", "5" }, - { TYPE_X86_CPU, "x-intel-pt-auto-level", "off" }, -}; -const size_t pc_compat_3_1_len = G_N_ELEMENTS(pc_compat_3_1); - /* * @PC_FW_DATA: * Size of the chunk of memory at the top of RAM for the BIOS ACPI tables diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 908de5e1ea..4e1dd436ab 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -271,9 +271,6 @@ extern const size_t pc_compat_4_1_len; extern GlobalProperty pc_compat_4_0[]; extern const size_t pc_compat_4_0_len; -extern GlobalProperty pc_compat_3_1[]; -extern const size_t pc_compat_3_1_len; - #define DEFINE_PC_MACHINE(suffix, namestr, initfn, optsfn) \ static void pc_machine_##suffix##_class_init(ObjectClass *oc, \ const void *data) \ From c935db70ee6077e72b3d3d45a0449897019fa70e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 29 Apr 2025 17:50:08 +0200 Subject: [PATCH 07/49] hw/i386/iommu: Remove IntelIOMMUState::dma_drain field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The IntelIOMMUState::dma_drain boolean was only set in the pc_compat_3_1[] array, via the 'dma-drain=off' property. We removed all machines using that array, lets remove that property and all the code around it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20260307152635.83893-7-philmd@linaro.org> --- hw/i386/intel_iommu.c | 11 +---------- include/hw/i386/intel_iommu.h | 1 - 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index d24ba989bf..f395fa248c 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -4205,7 +4205,6 @@ static const Property vtd_properties[] = { DEFINE_PROP_BOOL("snoop-control", IntelIOMMUState, snoop_control, false), DEFINE_PROP_BOOL("x-pasid-mode", IntelIOMMUState, pasid, false), DEFINE_PROP_BOOL("svm", IntelIOMMUState, svm, false), - DEFINE_PROP_BOOL("dma-drain", IntelIOMMUState, dma_drain, true), DEFINE_PROP_BOOL("stale-tm", IntelIOMMUState, stale_tm, false), DEFINE_PROP_BOOL("fs1gp", IntelIOMMUState, fs1gp, true), }; @@ -5000,11 +4999,8 @@ static void vtd_cap_init(IntelIOMMUState *s) X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s); s->cap = VTD_CAP_FRO | VTD_CAP_NFR | VTD_CAP_ND | VTD_ECAP_PT | - VTD_CAP_MAMV | VTD_CAP_PSI | VTD_CAP_SSLPS | + VTD_CAP_MAMV | VTD_CAP_PSI | VTD_CAP_SSLPS | VTD_CAP_DRAIN | VTD_CAP_ESRTPS | VTD_CAP_MGAW(s->aw_bits); - if (s->dma_drain) { - s->cap |= VTD_CAP_DRAIN; - } if (x86_iommu->dma_translation) { if (s->aw_bits >= VTD_HOST_AW_39BIT) { s->cap |= VTD_CAP_SAGAW_39bit; @@ -5582,11 +5578,6 @@ static bool vtd_decide_config(IntelIOMMUState *s, Error **errp) return false; } - if (s->scalable_mode && !s->dma_drain) { - error_setg(errp, "Need to set dma_drain for scalable mode"); - return false; - } - if (s->pasid && !s->scalable_mode) { error_setg(errp, "Need to set scalable mode for PASID"); return false; diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index 54c2b6b77a..e44ce31841 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -314,7 +314,6 @@ struct IntelIOMMUState { bool intr_eime; /* Extended interrupt mode enabled */ OnOffAuto intr_eim; /* Toggle for EIM cabability */ uint8_t aw_bits; /* Host/IOVA address width (in bits) */ - bool dma_drain; /* Whether DMA r/w draining enabled */ bool pasid; /* Whether to support PASID */ bool fs1gp; /* First Stage 1-GByte Page Support */ From b1173facb5d3c4abbb91ac5cf53b71dddc002459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 7 Mar 2026 12:55:47 +0100 Subject: [PATCH 08/49] target/i386/cpu: Remove X86CPU::intel_pt_auto_level field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The X86CPU::intel_pt_auto_level boolean was only set in the pc_compat_3_1[] array, via the 'x-intel-pt-auto-level=off' property. We removed all machines using that array, let's emove that property and all the code around it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20260307152635.83893-8-philmd@linaro.org> --- target/i386/cpu.c | 10 +--------- target/i386/cpu.h | 3 --- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index c77addd2c2..0a29ff805f 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -9565,13 +9565,7 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) /* Intel Processor Trace requires CPUID[0x14] */ if ((env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_INTEL_PT)) { - if (cpu->intel_pt_auto_level) { - x86_cpu_adjust_level(cpu, &cpu->env.cpuid_min_level, 0x14); - } else if (cpu->env.cpuid_min_level < 0x14) { - mark_unavailable_features(cpu, FEAT_7_0_EBX, - CPUID_7_0_EBX_INTEL_PT, - "Intel PT need CPUID leaf 0x14, please set by \"-cpu ...,intel-pt=on,min-level=0x14\""); - } + x86_cpu_adjust_level(cpu, &cpu->env.cpuid_min_level, 0x14); } /* @@ -10589,8 +10583,6 @@ static const Property x86_cpu_properties[] = { * to the specific Windows version being used." */ DEFINE_PROP_INT32("x-hv-max-vps", X86CPU, hv_max_vps, -1), - DEFINE_PROP_BOOL("x-intel-pt-auto-level", X86CPU, intel_pt_auto_level, - true), DEFINE_PROP_BOOL("x-l1-cache-per-thread", X86CPU, l1_cache_per_core, true), DEFINE_PROP_BOOL("x-force-cpuid-0x1f", X86CPU, force_cpuid_0x1f, false), diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 2b70d56e9b..5a62aa6157 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2457,9 +2457,6 @@ struct ArchCPU { /* Only advertise TOPOEXT features that AMD defines */ bool amd_topoext_features_only; - /* Enable auto level-increase for Intel Processor Trace leave */ - bool intel_pt_auto_level; - /* if true fill the top bits of the MTRR_PHYSMASKn variable range */ bool fill_mtrr_mask; From a861ffef237e81521a50c17018dd444eb8425200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 1 May 2025 23:31:56 +0200 Subject: [PATCH 09/49] hw/core/machine: Remove the hw_compat_3_1[] array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hw_compat_3_1[] array was only used by the pc-q35-3.1 and pc-i440fx-3.1 machines, which got removed. Remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20260307152635.83893-9-philmd@linaro.org> --- hw/core/machine.c | 17 ----------------- include/hw/core/boards.h | 3 --- 2 files changed, 20 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 162600425b..9a3d826c3c 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -221,23 +221,6 @@ GlobalProperty hw_compat_4_0[] = { }; const size_t hw_compat_4_0_len = G_N_ELEMENTS(hw_compat_4_0); -GlobalProperty hw_compat_3_1[] = { - { "pcie-root-port", "x-speed", "2_5" }, - { "pcie-root-port", "x-width", "1" }, - { "memory-backend-file", "x-use-canonical-path-for-ramblock-id", "true" }, - { "memory-backend-memfd", "x-use-canonical-path-for-ramblock-id", "true" }, - { "tpm-crb", "ppi", "false" }, - { "tpm-tis", "ppi", "false" }, - { "usb-kbd", "serial", "42" }, - { "usb-mouse", "serial", "42" }, - { "usb-tablet", "serial", "42" }, - { "virtio-blk-device", "discard", "false" }, - { "virtio-blk-device", "write-zeroes", "false" }, - { "virtio-balloon-device", "qemu-4-0-config-size", "false" }, - { "pcie-root-port-base", "disable-acs", "true" }, /* Added in 4.1 */ -}; -const size_t hw_compat_3_1_len = G_N_ELEMENTS(hw_compat_3_1); - MachineState *current_machine; static char *machine_get_kernel(Object *obj, Error **errp) diff --git a/include/hw/core/boards.h b/include/hw/core/boards.h index e15dbcea83..9155e0cc9e 100644 --- a/include/hw/core/boards.h +++ b/include/hw/core/boards.h @@ -867,7 +867,4 @@ extern const size_t hw_compat_4_1_len; extern GlobalProperty hw_compat_4_0[]; extern const size_t hw_compat_4_0_len; -extern GlobalProperty hw_compat_3_1[]; -extern const size_t hw_compat_3_1_len; - #endif From 0450e9d7bdd2a89313516680d18adec1962d5d18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 7 Mar 2026 12:57:28 +0100 Subject: [PATCH 10/49] hw/pci-bridge/pcie_rp: Remove PCIESlot::disable_acs field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PCIESlot::disable_acs boolean was only set in the hw_compat_3_1[] array, via the 'disable-acs=true' property. We removed all machines using that array, lets remove that property and all the code around it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20260307152635.83893-10-philmd@linaro.org> --- hw/pci-bridge/pcie_root_port.c | 3 +-- include/hw/pci/pcie_port.h | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/hw/pci-bridge/pcie_root_port.c b/hw/pci-bridge/pcie_root_port.c index fe3ced5685..7c3e78010b 100644 --- a/hw/pci-bridge/pcie_root_port.c +++ b/hw/pci-bridge/pcie_root_port.c @@ -117,7 +117,7 @@ static void rp_realize(PCIDevice *d, Error **errp) pcie_aer_root_init(d); rp_aer_vector_update(d); - if (rpc->acs_offset && !s->disable_acs) { + if (rpc->acs_offset) { pcie_acs_init(d, rpc->acs_offset); } return; @@ -151,7 +151,6 @@ static void rp_exit(PCIDevice *d) static const Property rp_props[] = { DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present, QEMU_PCIE_SLTCAP_PCP_BITNR, true), - DEFINE_PROP_BOOL("disable-acs", PCIESlot, disable_acs, false), }; static void rp_instance_post_init(Object *obj) diff --git a/include/hw/pci/pcie_port.h b/include/hw/pci/pcie_port.h index 53cd64c5ed..b28af067a6 100644 --- a/include/hw/pci/pcie_port.h +++ b/include/hw/pci/pcie_port.h @@ -60,9 +60,6 @@ struct PCIESlot { PCIExpLinkWidth width; bool flitmode; - /* Disable ACS (really for a pcie_root_port) */ - bool disable_acs; - /* Indicates whether any type of hot-plug is allowed on the slot */ bool hotplug; From 75afdc1047fb40f31975ff41a5f5909bf27f3af7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 29 Apr 2025 17:51:09 +0200 Subject: [PATCH 11/49] hw/i386/pc: Remove deprecated pc-q35-4.0[.1] and pc-i440fx-4.0 machines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These machines has been supported for a period of more than 6 years. According to our versioned machine support policy (see commit ce80c4fa6ff "docs: document special exception for machine type deprecation & removal") they can now be removed. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20260307154202.86309-2-philmd@linaro.org> --- hw/i386/pc_piix.c | 11 ----------- hw/i386/pc_q35.c | 25 ------------------------- 2 files changed, 36 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 2b94c47244..4d71e0d51a 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -644,17 +644,6 @@ static void pc_i440fx_machine_4_1_options(MachineClass *m) DEFINE_I440FX_MACHINE(4, 1); -static void pc_i440fx_machine_4_0_options(MachineClass *m) -{ - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_i440fx_machine_4_1_options(m); - pcmc->default_cpu_version = CPU_VERSION_LEGACY; - compat_props_add(m->compat_props, hw_compat_4_0, hw_compat_4_0_len); - compat_props_add(m->compat_props, pc_compat_4_0, pc_compat_4_0_len); -} - -DEFINE_I440FX_MACHINE(4, 0); - #ifdef CONFIG_XEN static void xenfv_machine_4_2_options(MachineClass *m) { diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 52b8064b4d..cb23322f5a 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -575,28 +575,3 @@ static void pc_q35_machine_4_1_options(MachineClass *m) } DEFINE_Q35_MACHINE(4, 1); - -static void pc_q35_machine_4_0_1_options(MachineClass *m) -{ - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_q35_machine_4_1_options(m); - pcmc->default_cpu_version = CPU_VERSION_LEGACY; - /* - * This is the default machine for the 4.0-stable branch. It is basically - * a 4.0 that doesn't use split irqchip by default. It MUST hence apply the - * 4.0 compat props. - */ - compat_props_add(m->compat_props, hw_compat_4_0, hw_compat_4_0_len); - compat_props_add(m->compat_props, pc_compat_4_0, pc_compat_4_0_len); -} - -DEFINE_Q35_MACHINE_BUGFIX(4, 0, 1); - -static void pc_q35_machine_4_0_options(MachineClass *m) -{ - pc_q35_machine_4_0_1_options(m); - m->default_kernel_irqchip_split = true; - /* Compat props are applied by the 4.0.1 machine */ -} - -DEFINE_Q35_MACHINE(4, 0); From aa711b58e9224e388d79495961fc62a42be80ba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 29 Apr 2025 17:52:38 +0200 Subject: [PATCH 12/49] hw/i386/pc: Remove pc_compat_4_0[] array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pc_compat_4_0[] array was only used by the pc-q35-4.0, pc-q35-4.0.1 and pc-i440fx-4.0 machines, which got removed. Remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20260307154202.86309-4-philmd@linaro.org> --- hw/i386/pc.c | 3 --- include/hw/i386/pc.h | 3 --- 2 files changed, 6 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index f33e3f7265..addf602da0 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -177,9 +177,6 @@ const size_t pc_compat_4_2_len = G_N_ELEMENTS(pc_compat_4_2); GlobalProperty pc_compat_4_1[] = {}; const size_t pc_compat_4_1_len = G_N_ELEMENTS(pc_compat_4_1); -GlobalProperty pc_compat_4_0[] = {}; -const size_t pc_compat_4_0_len = G_N_ELEMENTS(pc_compat_4_0); - /* * @PC_FW_DATA: * Size of the chunk of memory at the top of RAM for the BIOS ACPI tables diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 4e1dd436ab..22325324d0 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -268,9 +268,6 @@ extern const size_t pc_compat_4_2_len; extern GlobalProperty pc_compat_4_1[]; extern const size_t pc_compat_4_1_len; -extern GlobalProperty pc_compat_4_0[]; -extern const size_t pc_compat_4_0_len; - #define DEFINE_PC_MACHINE(suffix, namestr, initfn, optsfn) \ static void pc_machine_##suffix##_class_init(ObjectClass *oc, \ const void *data) \ From f5c74c2a49d849415247a996ec0e01835af28a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 2 May 2025 00:00:23 +0200 Subject: [PATCH 13/49] hw/core/machine: Remove the hw_compat_4_0[] array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hw_compat_4_0[] array was only used by the pc-q35-4.0 and pc-i440fx-4.0 machines, which got removed. Remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20260307154202.86309-5-philmd@linaro.org> --- hw/core/machine.c | 12 ------------ include/hw/core/boards.h | 3 --- 2 files changed, 15 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 9a3d826c3c..a14ad05b9a 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -209,18 +209,6 @@ GlobalProperty hw_compat_4_1[] = { }; const size_t hw_compat_4_1_len = G_N_ELEMENTS(hw_compat_4_1); -GlobalProperty hw_compat_4_0[] = { - { "VGA", "edid", "false" }, - { "secondary-vga", "edid", "false" }, - { "bochs-display", "edid", "false" }, - { "virtio-vga", "edid", "false" }, - { "virtio-gpu-device", "edid", "false" }, - { "virtio-device", "use-started", "false" }, - { "virtio-balloon-device", "qemu-4-0-config-size", "true" }, - { "pl031", "migrate-tick-offset", "false" }, -}; -const size_t hw_compat_4_0_len = G_N_ELEMENTS(hw_compat_4_0); - MachineState *current_machine; static char *machine_get_kernel(Object *obj, Error **errp) diff --git a/include/hw/core/boards.h b/include/hw/core/boards.h index 9155e0cc9e..f85f31bd90 100644 --- a/include/hw/core/boards.h +++ b/include/hw/core/boards.h @@ -864,7 +864,4 @@ extern const size_t hw_compat_4_2_len; extern GlobalProperty hw_compat_4_1[]; extern const size_t hw_compat_4_1_len; -extern GlobalProperty hw_compat_4_0[]; -extern const size_t hw_compat_4_0_len; - #endif From fb66dbbc4d7017e88f3a82a949c82570088d8d82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 2 May 2025 00:01:29 +0200 Subject: [PATCH 14/49] hw/virtio: Remove VirtIOBalloon::qemu_4_0_config_size field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VirtIOBalloon::qemu_4_0_config_size boolean was only set in the hw_compat_4_0[] array, via the 'qemu-4-0-config-size=true' property. We removed all machines using that array, lets remove that property. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20260307154202.86309-6-philmd@linaro.org> --- hw/virtio/virtio-balloon.c | 5 ----- include/hw/virtio/virtio-balloon.h | 1 - 2 files changed, 6 deletions(-) diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 38bf1e84a1..4c5f486ba2 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -708,9 +708,6 @@ static size_t virtio_balloon_config_size(VirtIOBalloon *s) { uint64_t features = s->host_features; - if (s->qemu_4_0_config_size) { - return sizeof(struct virtio_balloon_config); - } if (virtio_has_feature(features, VIRTIO_BALLOON_F_PAGE_POISON)) { return sizeof(struct virtio_balloon_config); } @@ -1054,8 +1051,6 @@ static const Property virtio_balloon_properties[] = { * is disabled, resulting in QEMU 3.1 migration incompatibility. This * property retains this quirk for QEMU 4.1 machine types. */ - DEFINE_PROP_BOOL("qemu-4-0-config-size", VirtIOBalloon, - qemu_4_0_config_size, false), DEFINE_PROP_LINK("iothread", VirtIOBalloon, iothread, TYPE_IOTHREAD, IOThread *), }; diff --git a/include/hw/virtio/virtio-balloon.h b/include/hw/virtio/virtio-balloon.h index 79ac194cce..abbf339718 100644 --- a/include/hw/virtio/virtio-balloon.h +++ b/include/hw/virtio/virtio-balloon.h @@ -70,7 +70,6 @@ struct VirtIOBalloon { int64_t stats_poll_interval; uint32_t host_features; - bool qemu_4_0_config_size; uint32_t poison_val; /* State of the resettable container */ From 59de9b6fa759fd4d0641ba39de442fa6f8e87413 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 2 May 2025 00:03:16 +0200 Subject: [PATCH 15/49] hw/rtc/pl031: : Remove PL031State::migrate_tick_offset field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PL031State::migrate_tick_offset boolean was only set in the hw_compat_4_0[] array, via the 'migrate-tick-offset=false' property. We removed all machines using that array, lets remove that property, along with the pl031_properties[] array which is now empty. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-Id: <20260307154202.86309-7-philmd@linaro.org> --- hw/rtc/pl031.c | 22 ---------------------- include/hw/rtc/pl031.h | 1 - 2 files changed, 23 deletions(-) diff --git a/hw/rtc/pl031.c b/hw/rtc/pl031.c index b56877c3a2..09d1b15c77 100644 --- a/hw/rtc/pl031.c +++ b/hw/rtc/pl031.c @@ -278,18 +278,10 @@ static int pl031_tick_offset_post_load(void *opaque, int version_id) return 0; } -static bool pl031_tick_offset_needed(void *opaque) -{ - PL031State *s = opaque; - - return s->migrate_tick_offset; -} - static const VMStateDescription vmstate_pl031_tick_offset = { .name = "pl031/tick-offset", .version_id = 1, .minimum_version_id = 1, - .needed = pl031_tick_offset_needed, .post_load = pl031_tick_offset_post_load, .fields = (const VMStateField[]) { VMSTATE_UINT32(tick_offset, PL031State), @@ -319,25 +311,11 @@ static const VMStateDescription vmstate_pl031 = { } }; -static const Property pl031_properties[] = { - /* - * True to correctly migrate the tick offset of the RTC. False to - * obtain backward migration compatibility with older QEMU versions, - * at the expense of the guest RTC going backwards compared with the - * host RTC when the VM is saved/restored if using -rtc host. - * (Even if set to 'true' older QEMU can migrate forward to newer QEMU; - * 'false' also permits newer QEMU to migrate to older QEMU.) - */ - DEFINE_PROP_BOOL("migrate-tick-offset", - PL031State, migrate_tick_offset, true), -}; - static void pl031_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_pl031; - device_class_set_props(dc, pl031_properties); } static const TypeInfo pl031_info = { diff --git a/include/hw/rtc/pl031.h b/include/hw/rtc/pl031.h index c8b26c2f00..75779c6312 100644 --- a/include/hw/rtc/pl031.h +++ b/include/hw/rtc/pl031.h @@ -36,7 +36,6 @@ struct PL031State { uint32_t tick_offset_vmstate; uint32_t tick_offset; bool tick_offset_migrated; - bool migrate_tick_offset; uint32_t mr; uint32_t lr; From e96ff0266900b37aa30c5ea9d662db94eae0f3fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 22 Jan 2026 15:31:40 +0400 Subject: [PATCH 16/49] tests: fix leaks in dbus-display-test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Close the connections and complete the method invocations. Thanks ASAN for the report. Signed-off-by: Marc-André Lureau Acked-by: Fabiano Rosas Message-ID: <20260122113144.2046899-2-marcandre.lureau@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- tests/qtest/dbus-display-test.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/qtest/dbus-display-test.c b/tests/qtest/dbus-display-test.c index 1d5951b711..5773776cad 100644 --- a/tests/qtest/dbus-display-test.c +++ b/tests/qtest/dbus-display-test.c @@ -76,6 +76,7 @@ test_dbus_display_vm(void) qemu_dbus_display1_vm_get_name(QEMU_DBUS_DISPLAY1_VM(vm)), ==, "dbus-test"); + g_clear_object(&conn); qtest_quit(qts); } @@ -97,6 +98,8 @@ static gboolean listener_handle_scanout( GVariant *arg_data, TestDBusConsoleRegister *test) { + qemu_dbus_display1_listener_complete_scanout(object, invocation); + if (!test->with_map) { g_main_loop_quit(test->loop); } @@ -131,6 +134,9 @@ static gboolean listener_handle_scanout_map( g_assert_no_errno(addr == MAP_FAILED ? -1 : 0); g_assert_no_errno(munmap(addr, len)); + qemu_dbus_display1_listener_unix_map_complete_scanout_map(object, invocation, + NULL); + g_main_loop_quit(test->loop); close(fd); @@ -287,6 +293,7 @@ test_dbus_display_console(const void* data) g_clear_object(&test.server); g_clear_object(&test.listener_conn); + g_clear_object(&conn); qtest_quit(qts); } @@ -322,6 +329,7 @@ test_dbus_display_keyboard(void) &err); if (g_error_matches(err, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) { g_test_skip("The VM doesn't have a console!"); + g_clear_object(&conn); qtest_quit(qts); return; } @@ -348,6 +356,7 @@ test_dbus_display_keyboard(void) g_assert_cmpint(qemu_dbus_display1_keyboard_get_modifiers( QEMU_DBUS_DISPLAY1_KEYBOARD(keyboard)), ==, 0); + g_clear_object(&conn); qtest_quit(qts); } From 59bf093a64cff4eca5bc7c7d9afa8d58b7eadd50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 22 Jan 2026 15:31:41 +0400 Subject: [PATCH 17/49] ui/dbus: fix leak regression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For some reason, during refactoring, an extra reference leak was introduced. Fixes: commit 7945576cf2 ("ui/dbus: factor out sending a scanout") Signed-off-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260122113144.2046899-3-marcandre.lureau@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- ui/dbus-listener.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index 52e041edb0..407719fc1e 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -815,8 +815,7 @@ static void ddl_scanout(DBusDisplayListener *ddl) qemu_dbus_display1_listener_call_scanout( ddl->proxy, surface_width(ddl->ds), surface_height(ddl->ds), surface_stride(ddl->ds), surface_format(ddl->ds), v_data, - G_DBUS_CALL_FLAGS_NONE, DBUS_DEFAULT_TIMEOUT, NULL, NULL, - g_object_ref(ddl)); + G_DBUS_CALL_FLAGS_NONE, DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); } static void dbus_gfx_update(DisplayChangeListener *dcl, From 4e86363a288ef217081ae1868425c27bdc5bf93d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 22 Jan 2026 15:31:42 +0400 Subject: [PATCH 18/49] ui/dbus: fix Unix.Map proxy leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: commit 48b7ef0f0 ("ui/dbus: implement Unix.Map") Signed-off-by: Marc-André Lureau Reviewed-by: Peter Maydell Message-ID: <20260122113144.2046899-4-marcandre.lureau@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- ui/dbus-listener.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index 407719fc1e..46264839d8 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -960,8 +960,8 @@ dbus_display_listener_dispose(GObject *object) g_clear_object(&ddl->conn); g_clear_pointer(&ddl->bus_name, g_free); g_clear_object(&ddl->proxy); -#ifdef WIN32 g_clear_object(&ddl->map_proxy); +#ifdef WIN32 g_clear_object(&ddl->d3d11_proxy); g_clear_pointer(&ddl->peer_process, CloseHandle); #ifdef CONFIG_PIXMAN From 639adeabb3a8db485a950840f81d0208656a06e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 22 Jan 2026 15:31:43 +0400 Subject: [PATCH 19/49] ui/dbus: fix pixman cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moved pixman_region32_fini() outside the WIN32 block so it's called on all platforms. Fixes: commit 7007e98c ("ui/dbus: implement damage regions for GL") Signed-off-by: Marc-André Lureau Reviewed-by: Peter Maydell Message-ID: <20260122113144.2046899-5-marcandre.lureau@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- ui/dbus-listener.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index 46264839d8..3e2b4adf41 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -964,15 +964,15 @@ dbus_display_listener_dispose(GObject *object) #ifdef WIN32 g_clear_object(&ddl->d3d11_proxy); g_clear_pointer(&ddl->peer_process, CloseHandle); -#ifdef CONFIG_PIXMAN - pixman_region32_fini(&ddl->gl_damage); -#endif #ifdef CONFIG_OPENGL egl_fb_destroy(&ddl->fb); #endif #else /* !WIN32 */ g_clear_object(&ddl->scanout_dmabuf_v2_proxy); #endif +#ifdef CONFIG_PIXMAN + pixman_region32_fini(&ddl->gl_damage); +#endif G_OBJECT_CLASS(dbus_display_listener_parent_class)->dispose(object); } From 6151922f0ebac1a7fa8647800ccee06f647d586e Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Wed, 4 Mar 2026 15:16:54 +0900 Subject: [PATCH 20/49] coreaudio: Remove unnecessary explicit casts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit coreaudio had unnecessary explicit casts and they had extra whitespaces around them so remove them. Signed-off-by: Akihiko Odaki Reviewed-by: Marc-André Lureau Message-ID: <20260304-coreaudio-v8-1-bf1d40731e73@rsg.ci.i.u-tokyo.ac.jp> Signed-off-by: Philippe Mathieu-Daudé --- audio/coreaudio.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/audio/coreaudio.m b/audio/coreaudio.m index a28fa77d73..cbda4f1436 100644 --- a/audio/coreaudio.m +++ b/audio/coreaudio.m @@ -310,7 +310,7 @@ static OSStatus audioDeviceIOProc( UInt32 frameCount, pending_frames; void *out = outOutputData->mBuffers[0].mData; HWVoiceOut *hw = hwptr; - coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr; + coreaudioVoiceOut *core = hwptr; size_t len; if (coreaudio_buf_lock (core, "audioDeviceIOProc")) { @@ -394,11 +394,11 @@ static OSStatus init_out_device(coreaudioVoiceOut *core) } if (frameRange.mMinimum > core->frameSizeSetting) { - core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum; + core->audioDevicePropertyBufferFrameSize = frameRange.mMinimum; warn_report("coreaudio: Upsizing buffer frames to %f", frameRange.mMinimum); } else if (frameRange.mMaximum < core->frameSizeSetting) { - core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum; + core->audioDevicePropertyBufferFrameSize = frameRange.mMaximum; warn_report("coreaudio: Downsizing buffer frames to %f", frameRange.mMaximum); } else { From bac986a6709bcf38445877cbfa326788b86a7297 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Wed, 4 Mar 2026 15:16:55 +0900 Subject: [PATCH 21/49] coreaudio: Remove extra whitespaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove extra whitespaces around parentheses. Signed-off-by: Akihiko Odaki Reviewed-by: Marc-André Lureau Message-ID: <20260304-coreaudio-v8-2-bf1d40731e73@rsg.ci.i.u-tokyo.ac.jp> Signed-off-by: Philippe Mathieu-Daudé --- audio/coreaudio.m | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/audio/coreaudio.m b/audio/coreaudio.m index cbda4f1436..bc9ab7477b 100644 --- a/audio/coreaudio.m +++ b/audio/coreaudio.m @@ -272,7 +272,7 @@ static int coreaudio_buf_unlock(coreaudioVoiceOut *core, const char *fn_name) #define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \ static ret_type glue(coreaudio_, name)args_decl \ { \ - coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; \ + coreaudioVoiceOut *core = (coreaudioVoiceOut *)hw; \ ret_type ret; \ \ if (coreaudio_buf_lock(core, "coreaudio_" #name)) { \ @@ -313,13 +313,13 @@ static OSStatus audioDeviceIOProc( coreaudioVoiceOut *core = hwptr; size_t len; - if (coreaudio_buf_lock (core, "audioDeviceIOProc")) { + if (coreaudio_buf_lock(core, "audioDeviceIOProc")) { inInputTime = 0; return 0; } if (inDevice != core->outputDeviceID) { - coreaudio_buf_unlock (core, "audioDeviceIOProc(old device)"); + coreaudio_buf_unlock(core, "audioDeviceIOProc(old device)"); return 0; } @@ -329,7 +329,7 @@ static OSStatus audioDeviceIOProc( /* if there are not enough samples, set signal and return */ if (pending_frames < frameCount) { inInputTime = 0; - coreaudio_buf_unlock (core, "audioDeviceIOProc(empty)"); + coreaudio_buf_unlock(core, "audioDeviceIOProc(empty)"); return 0; } @@ -349,7 +349,7 @@ static OSStatus audioDeviceIOProc( out += write_len; } - coreaudio_buf_unlock (core, "audioDeviceIOProc"); + coreaudio_buf_unlock(core, "audioDeviceIOProc"); return 0; } @@ -563,7 +563,7 @@ static OSStatus handle_voice_change( static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as) { OSStatus status; - coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; + coreaudioVoiceOut *core = (coreaudioVoiceOut *)hw; int err; Audiodev *dev = hw->s->dev; AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out; @@ -579,7 +579,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as) obt_as = *as; as = &obt_as; as->fmt = AUDIO_FORMAT_F32; - audio_pcm_init_info (&hw->info, as); + audio_pcm_init_info(&hw->info, as); core->frameSizeSetting = audio_buffer_frames( qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610); @@ -615,7 +615,7 @@ static void coreaudio_fini_out (HWVoiceOut *hw) { OSStatus status; int err; - coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; + coreaudioVoiceOut *core = (coreaudioVoiceOut *)hw; status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &voice_addr, @@ -636,7 +636,7 @@ static void coreaudio_fini_out (HWVoiceOut *hw) static void coreaudio_enable_out(HWVoiceOut *hw, bool enable) { - coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; + coreaudioVoiceOut *core = (coreaudioVoiceOut *)hw; core->enabled = enable; update_device_playback_state(core); From 2fd2f50e9b42362c8dc10bc38c21a1fb34eaa66b Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Thu, 5 Mar 2026 04:25:22 +0000 Subject: [PATCH 22/49] virtio-snd: remove a redundant memory allocation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change also makes its lifetime explicit and prevents NULL dereferences. Signed-off-by: Roman Kiryanov Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260305042523.335493-1-rkir@google.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/audio/virtio-snd.c | 53 ++++++++++++++++------------------- include/hw/audio/virtio-snd.h | 2 +- 2 files changed, 25 insertions(+), 30 deletions(-) diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c index b8376433f7..0f78cec967 100644 --- a/hw/audio/virtio-snd.c +++ b/hw/audio/virtio-snd.c @@ -130,7 +130,7 @@ static VirtIOSoundPCMStream *virtio_snd_pcm_get_stream(VirtIOSound *s, uint32_t stream_id) { return stream_id >= s->snd_conf.streams ? NULL : - s->pcm->streams[stream_id]; + s->pcm.streams[stream_id]; } /* @@ -143,7 +143,7 @@ static virtio_snd_pcm_set_params *virtio_snd_pcm_get_params(VirtIOSound *s, uint32_t stream_id) { return stream_id >= s->snd_conf.streams ? NULL - : &s->pcm->pcm_params[stream_id]; + : &s->pcm.pcm_params[stream_id]; } /* @@ -260,7 +260,7 @@ uint32_t virtio_snd_set_pcm_params(VirtIOSound *s, { virtio_snd_pcm_set_params *st_params; - if (stream_id >= s->snd_conf.streams || s->pcm->pcm_params == NULL) { + if (stream_id >= s->snd_conf.streams || s->pcm.pcm_params == NULL) { virtio_error(VIRTIO_DEVICE(s), "Streams have not been initialized.\n"); return cpu_to_le32(VIRTIO_SND_S_BAD_MSG); } @@ -424,8 +424,8 @@ static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id) virtio_snd_pcm_set_params *params; VirtIOSoundPCMStream *stream; - if (s->pcm->streams == NULL || - s->pcm->pcm_params == NULL || + if (s->pcm.streams == NULL || + s->pcm.pcm_params == NULL || stream_id >= s->snd_conf.streams) { return cpu_to_le32(VIRTIO_SND_S_BAD_MSG); } @@ -440,7 +440,7 @@ static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id) stream = g_new0(VirtIOSoundPCMStream, 1); stream->active = false; stream->id = stream_id; - stream->pcm = s->pcm; + stream->pcm = &s->pcm; stream->s = s; stream->latency_bytes = 0; qemu_mutex_init(&stream->queue_mutex); @@ -450,7 +450,7 @@ static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id) * stream_id >= s->snd_conf.streams was checked before so this is * in-bounds */ - s->pcm->streams[stream_id] = stream; + s->pcm.streams[stream_id] = stream; } virtio_snd_get_qemu_audsettings(&as, params); @@ -884,11 +884,11 @@ static void virtio_snd_handle_tx_xfer(VirtIODevice *vdev, VirtQueue *vq) stream_id = le32_to_cpu(hdr.stream_id); if (stream_id >= vsnd->snd_conf.streams - || vsnd->pcm->streams[stream_id] == NULL) { + || vsnd->pcm.streams[stream_id] == NULL) { goto tx_err; } - stream = vsnd->pcm->streams[stream_id]; + stream = vsnd->pcm.streams[stream_id]; if (stream->info.direction != VIRTIO_SND_D_OUTPUT) { goto tx_err; } @@ -966,11 +966,11 @@ static void virtio_snd_handle_rx_xfer(VirtIODevice *vdev, VirtQueue *vq) stream_id = le32_to_cpu(hdr.stream_id); if (stream_id >= vsnd->snd_conf.streams - || !vsnd->pcm->streams[stream_id]) { + || !vsnd->pcm.streams[stream_id]) { goto rx_err; } - stream = vsnd->pcm->streams[stream_id]; + stream = vsnd->pcm.streams[stream_id]; if (stream == NULL || stream->info.direction != VIRTIO_SND_D_INPUT) { goto rx_err; } @@ -1064,11 +1064,10 @@ static void virtio_snd_realize(DeviceState *dev, Error **errp) vsnd->vmstate = qemu_add_vm_change_state_handler(virtio_snd_vm_state_change, vsnd); - vsnd->pcm = g_new0(VirtIOSoundPCM, 1); - vsnd->pcm->snd = vsnd; - vsnd->pcm->streams = + vsnd->pcm.snd = vsnd; + vsnd->pcm.streams = g_new0(VirtIOSoundPCMStream *, vsnd->snd_conf.streams); - vsnd->pcm->pcm_params = + vsnd->pcm.pcm_params = g_new0(virtio_snd_pcm_set_params, vsnd->snd_conf.streams); virtio_init(vdev, VIRTIO_ID_SOUND, sizeof(virtio_snd_config)); @@ -1335,23 +1334,19 @@ static void virtio_snd_unrealize(DeviceState *dev) qemu_del_vm_change_state_handler(vsnd->vmstate); trace_virtio_snd_unrealize(vsnd); - if (vsnd->pcm) { - if (vsnd->pcm->streams) { - for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) { - stream = vsnd->pcm->streams[i]; - if (stream) { - virtio_snd_process_cmdq(stream->s); - virtio_snd_pcm_close(stream); - qemu_mutex_destroy(&stream->queue_mutex); - g_free(stream); - } + if (vsnd->pcm.streams) { + for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) { + stream = vsnd->pcm.streams[i]; + if (stream) { + virtio_snd_process_cmdq(stream->s); + virtio_snd_pcm_close(stream); + qemu_mutex_destroy(&stream->queue_mutex); + g_free(stream); } - g_free(vsnd->pcm->streams); } - g_free(vsnd->pcm->pcm_params); - g_free(vsnd->pcm); - vsnd->pcm = NULL; + g_free(vsnd->pcm.streams); } + g_free(vsnd->pcm.pcm_params); qemu_mutex_destroy(&vsnd->cmdq_mutex); virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_CONTROL]); virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_EVENT]); diff --git a/include/hw/audio/virtio-snd.h b/include/hw/audio/virtio-snd.h index 9560bac8b1..da2ba9675e 100644 --- a/include/hw/audio/virtio-snd.h +++ b/include/hw/audio/virtio-snd.h @@ -216,7 +216,7 @@ struct VirtIOSound { VirtQueue *queues[VIRTIO_SND_VQ_MAX]; uint64_t features; - VirtIOSoundPCM *pcm; + VirtIOSoundPCM pcm; AudioBackend *audio_be; VMChangeStateEntry *vmstate; virtio_snd_config snd_conf; From 248958526d9bbe47614165782c6357f5c3240f40 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Thu, 5 Mar 2026 04:25:23 +0000 Subject: [PATCH 23/49] virtio-snd: remove redundant fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VirtIOSoundPCM does not need a pointer to VirtIOSound because each VirtIOSoundPCMStream already carries a pointer to it. Signed-off-by: Roman Kiryanov Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260305042523.335493-2-rkir@google.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/audio/virtio-snd.c | 6 ++---- include/hw/audio/virtio-snd.h | 2 -- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c index 0f78cec967..fb5cff3866 100644 --- a/hw/audio/virtio-snd.c +++ b/hw/audio/virtio-snd.c @@ -402,10 +402,10 @@ static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream) if (stream) { virtio_snd_pcm_flush(stream); if (stream->info.direction == VIRTIO_SND_D_OUTPUT) { - audio_be_close_out(stream->pcm->snd->audio_be, stream->voice.out); + audio_be_close_out(stream->s->audio_be, stream->voice.out); stream->voice.out = NULL; } else if (stream->info.direction == VIRTIO_SND_D_INPUT) { - audio_be_close_in(stream->pcm->snd->audio_be, stream->voice.in); + audio_be_close_in(stream->s->audio_be, stream->voice.in); stream->voice.in = NULL; } } @@ -440,7 +440,6 @@ static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id) stream = g_new0(VirtIOSoundPCMStream, 1); stream->active = false; stream->id = stream_id; - stream->pcm = &s->pcm; stream->s = s; stream->latency_bytes = 0; qemu_mutex_init(&stream->queue_mutex); @@ -1064,7 +1063,6 @@ static void virtio_snd_realize(DeviceState *dev, Error **errp) vsnd->vmstate = qemu_add_vm_change_state_handler(virtio_snd_vm_state_change, vsnd); - vsnd->pcm.snd = vsnd; vsnd->pcm.streams = g_new0(VirtIOSoundPCMStream *, vsnd->snd_conf.streams); vsnd->pcm.pcm_params = diff --git a/include/hw/audio/virtio-snd.h b/include/hw/audio/virtio-snd.h index da2ba9675e..e28f1be5db 100644 --- a/include/hw/audio/virtio-snd.h +++ b/include/hw/audio/virtio-snd.h @@ -122,7 +122,6 @@ struct VirtIOSoundPCMBuffer { }; struct VirtIOSoundPCM { - VirtIOSound *snd; /* * PCM parameters are a separate field instead of a VirtIOSoundPCMStream * field, because the operation of PCM control requests is first @@ -135,7 +134,6 @@ struct VirtIOSoundPCM { }; struct VirtIOSoundPCMStream { - VirtIOSoundPCM *pcm; virtio_snd_pcm_info info; virtio_snd_pcm_set_params params; uint32_t id; From 2d23a976311ee7dbbd11518a48ab8e614338a143 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 5 Mar 2026 15:16:44 +0900 Subject: [PATCH 24/49] contrib/elf2dmp: Grow PDB URL buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The buffers used to construct a PDB URL overflow when the "age" property is greater than 0xf, so grow it. This also simplifies the logic of the URL construction to use one buffer instead of two to avoid the chore to synchronize the sizes of two buffers. Signed-off-by: Akihiko Odaki Reviewed-by: Peter Maydell Message-ID: <20260305-nvme-v4-1-b65b9de1839f@rsg.ci.i.u-tokyo.ac.jp> Signed-off-by: Philippe Mathieu-Daudé --- contrib/elf2dmp/main.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/contrib/elf2dmp/main.c b/contrib/elf2dmp/main.c index d046a72ae6..a62abadcc0 100644 --- a/contrib/elf2dmp/main.c +++ b/contrib/elf2dmp/main.c @@ -494,18 +494,6 @@ static bool pe_check_pdb_name(uint64_t base, void *start_addr, return !strcmp(pdb_name, PDB_NAME); } -static void pe_get_pdb_symstore_hash(OMFSignatureRSDS *rsds, char *hash) -{ - sprintf(hash, "%.08x%.04x%.04x%.02x%.02x", rsds->guid.a, rsds->guid.b, - rsds->guid.c, rsds->guid.d[0], rsds->guid.d[1]); - hash += 20; - for (unsigned int i = 0; i < 6; i++, hash += 2) { - sprintf(hash, "%.02x", rsds->guid.e[i]); - } - - sprintf(hash, "%.01x", rsds->age); -} - int main(int argc, char *argv[]) { int err = 1; @@ -517,9 +505,7 @@ int main(int argc, char *argv[]) uint64_t KernBase; void *nt_start_addr = NULL; WinDumpHeader64 header; - char pdb_hash[34]; - char pdb_url[] = SYM_URL_BASE PDB_NAME - "/0123456789ABCDEF0123456789ABCDEFx/" PDB_NAME; + g_autofree char *pdb_url = NULL; struct pdb_reader pdb; uint64_t KdDebuggerDataBlock; KDDEBUGGER_DATA64 *kdbg; @@ -583,9 +569,21 @@ int main(int argc, char *argv[]) printf("KernBase = 0x%016"PRIx64", signature is \'%.2s\'\n", KernBase, (char *)nt_start_addr); - pe_get_pdb_symstore_hash(&rsds, pdb_hash); + pdb_url = g_strdup_printf("%s" + "%.08x%.04x%.04x" + "%.02x%.02x" + "%.02x%.02x" + "%.02x%.02x" + "%.02x%.02x%.01x" + "%s", + SYM_URL_BASE PDB_NAME "/", + rsds.guid.a, rsds.guid.b, rsds.guid.c, + rsds.guid.d[0], rsds.guid.d[1], + rsds.guid.e[0], rsds.guid.e[1], + rsds.guid.e[2], rsds.guid.e[3], + rsds.guid.e[4], rsds.guid.e[5], rsds.age, + "/" PDB_NAME); - sprintf(pdb_url, "%s%s/%s/%s", SYM_URL_BASE, PDB_NAME, pdb_hash, PDB_NAME); printf("PDB URL is %s\n", pdb_url); if (!download_url(PDB_NAME, pdb_url)) { From 1785bf9685ecb424d0f80a4472aa0c623a07a88c Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 5 Mar 2026 15:16:45 +0900 Subject: [PATCH 25/49] vfio/pci: Grow buffer in vfio_pci_host_match() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each field of PCIHostDeviceAddress is an unsigned int, therefore while a valid address is limited to 13 characters, an invalid address could exceed the specified format, up to: ffffffff:ffffffff:ffffffff.ffffffff This requires 36 characters with the terminator. Signed-off-by: Akihiko Odaki Reviewed-by: Alex Williamson Message-ID: <20260305-nvme-v4-2-b65b9de1839f@rsg.ci.i.u-tokyo.ac.jp> Signed-off-by: Philippe Mathieu-Daudé --- hw/vfio/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index c89f3fbea3..94c174a773 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2739,7 +2739,7 @@ void vfio_pci_post_reset(VFIOPCIDevice *vdev) bool vfio_pci_host_match(PCIHostDeviceAddress *addr, const char *name) { - char tmp[13]; + char tmp[36]; sprintf(tmp, "%04x:%02x:%02x.%1x", addr->domain, addr->bus, addr->slot, addr->function); From 2a58162a1e4545cf26e77c139c46d7a644b2318b Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 5 Mar 2026 15:16:46 +0900 Subject: [PATCH 26/49] tests: Clean up double comparisons to avoid compiler warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To enable -Wformat-overflow=2, we need to clean up a couple of false positives: [2/5] Compiling C object tests/unit/test-qobject-output-visitor.p/test-qobject-output-visitor.c.o ../tests/unit/test-qobject-output-visitor.c: In function ‘test_visitor_out_list_struct’: ../tests/unit/test-qobject-output-visitor.c:577:28: error: ‘%.6f’ directive writing between 3 and 317 bytes into a region of size 32 [-Werror=format-overflow=] 577 | sprintf(expected, "%.6f", (double)i / 3); | ^~~~ ../tests/unit/test-qobject-output-visitor.c:577:27: note: assuming directive output of 8 bytes 577 | sprintf(expected, "%.6f", (double)i / 3); | ^~~~~~ In file included from /usr/include/stdio.h:970, from /home/me/q/var/qemu/include/qemu/osdep.h:114, from ../tests/unit/test-qobject-output-visitor.c:13: In function ‘sprintf’, inlined from ‘test_visitor_out_list_struct’ at ../tests/unit/test-qobject-output-visitor.c:577:9: /usr/include/bits/stdio2.h:30:10: note: ‘__builtin___sprintf_chk’ output between 4 and 318 bytes into a destination of size 32 30 | return __builtin___sprintf_chk (__s, __USE_FORTIFY_LEVEL - 1, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 31 | __glibc_objsize (__s), __fmt, | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 32 | __va_arg_pack ()); | ~~~~~~~~~~~~~~~~~ cc1: all warnings being treated as errors FAILED: tests/unit/test-qobject-output-visitor.p/test-qobject-output-visitor.c.o These buffers cannot actually overflow because the doubles are between 0 and 31.0/3 inclusive. However, formatting doubles just to compare them is silly. Compare them directly instead. To avoid potential rounding trouble, change the numbers tested to be representable exactly in double. Signed-off-by: Akihiko Odaki Reviewed-by: Markus Armbruster Message-ID: <20260305-nvme-v4-3-b65b9de1839f@rsg.ci.i.u-tokyo.ac.jp> Signed-off-by: Philippe Mathieu-Daudé --- tests/unit/test-qobject-input-visitor.c | 8 ++------ tests/unit/test-qobject-output-visitor.c | 7 ++----- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/tests/unit/test-qobject-input-visitor.c b/tests/unit/test-qobject-input-visitor.c index 84bdcdf702..beee11db4e 100644 --- a/tests/unit/test-qobject-input-visitor.c +++ b/tests/unit/test-qobject-input-visitor.c @@ -500,7 +500,7 @@ static void test_visitor_in_list_struct(TestInputVisitorData *data, g_string_append_printf(json, "'number': ["); sep = ""; for (i = 0; i < 32; i++) { - g_string_append_printf(json, "%s%f", sep, (double)i / 3); + g_string_append_printf(json, "%s%f", sep, (double)i / FLT_RADIX); sep = ", "; } g_string_append_printf(json, "], "); @@ -583,11 +583,7 @@ static void test_visitor_in_list_struct(TestInputVisitorData *data, i = 0; for (num_list = arrs->number; num_list; num_list = num_list->next) { - char expected[32], actual[32]; - - sprintf(expected, "%.6f", (double)i / 3); - sprintf(actual, "%.6f", num_list->value); - g_assert_cmpstr(expected, ==, actual); + g_assert_cmpfloat(num_list->value, ==, (double)i / FLT_RADIX); i++; } diff --git a/tests/unit/test-qobject-output-visitor.c b/tests/unit/test-qobject-output-visitor.c index 407ab9ed50..3c47b28f66 100644 --- a/tests/unit/test-qobject-output-visitor.c +++ b/tests/unit/test-qobject-output-visitor.c @@ -538,7 +538,7 @@ static void test_visitor_out_list_struct(TestOutputVisitorData *data, } for (i = 31; i >= 0; i--) { - QAPI_LIST_PREPEND(arrs->number, (double)i / 3); + QAPI_LIST_PREPEND(arrs->number, (double)i / FLT_RADIX); } for (i = 31; i >= 0; i--) { @@ -571,12 +571,9 @@ static void test_visitor_out_list_struct(TestOutputVisitorData *data, i = 0; QLIST_FOREACH_ENTRY(qlist, e) { QNum *qvalue = qobject_to(QNum, qlist_entry_obj(e)); - char expected[32], actual[32]; g_assert(qvalue); - sprintf(expected, "%.6f", (double)i / 3); - sprintf(actual, "%.6f", qnum_get_double(qvalue)); - g_assert_cmpstr(actual, ==, expected); + g_assert_cmpfloat(qnum_get_double(qvalue), ==, (double)i / FLT_RADIX); i++; } From 4efd93949f5a44238fb7446af41ae239930ce3a5 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 5 Mar 2026 15:16:47 +0900 Subject: [PATCH 27/49] meson: Add -Wformat-overflow=2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://gcc.gnu.org/onlinedocs/gcc-15.2.0/gcc/Warning-Options.html > Level 2 warns also about calls that might overflow the destination > buffer given an argument of sufficient length or magnitude. At level > 2, unknown numeric arguments are assumed to have the minimum > representable value for signed types with a precision greater than 1, > and the maximum representable value otherwise. Unknown string > arguments whose length cannot be assumed to be bounded either by the > directive’s precision, or by a finite set of string literals they may > evaluate to, or the character array they may point to, are assumed to > be 1 character long. Signed-off-by: Akihiko Odaki Reviewed-by: Peter Maydell Message-ID: <20260305-nvme-v4-4-b65b9de1839f@rsg.ci.i.u-tokyo.ac.jp> Signed-off-by: Philippe Mathieu-Daudé --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index 102f0cbb13..f45885f05a 100644 --- a/meson.build +++ b/meson.build @@ -694,6 +694,7 @@ warn_flags = [ '-Wempty-body', '-Wendif-labels', '-Wexpansion-to-defined', + '-Wformat-overflow=2', '-Wformat-security', '-Wformat-y2k', '-Wignored-qualifiers', From a1596cdc37a9da9cca30f211b8baee62badd6ab0 Mon Sep 17 00:00:00 2001 From: Peter Foley Date: Thu, 5 Mar 2026 11:04:56 -0500 Subject: [PATCH 28/49] net/passt: Only kill passt if it wrote a pidfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid killing qemu if passt failed before writing a pidfile. pid is initialized to 0, so calling pid in this scenario would kill the entire process group. Reviewed-by: Laurent Vivier Signed-off-by: Peter Foley Message-ID: <20260305-passt-v2-1-f0582198afc0@google.com> Signed-off-by: Philippe Mathieu-Daudé --- net/passt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/passt.c b/net/passt.c index 9ed811a514..b3d4b71314 100644 --- a/net/passt.c +++ b/net/passt.c @@ -102,7 +102,9 @@ static void net_passt_cleanup(NetClientState *nc) } #endif - kill(s->pid, SIGTERM); + if (s->pid > 0) { + kill(s->pid, SIGTERM); + } if (g_remove(s->pidfile) != 0) { warn_report("Failed to remove passt pidfile %s: %s", s->pidfile, strerror(errno)); From d89f7c8194690ab5979ab4802de67e919126be9f Mon Sep 17 00:00:00 2001 From: Peter Foley Date: Thu, 5 Mar 2026 11:04:57 -0500 Subject: [PATCH 29/49] net/passt: Don't try to read the pidfile if passt got a signal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit g_subprocess_get_if_exited returns false if passt was killed by a signal, so we fall through to trying to read the pidfile. Update the error when passt exits to include the exit code. Reviewed-by: Laurent Vivier Signed-off-by: Peter Foley Message-ID: <20260305-passt-v2-2-f0582198afc0@google.com> Signed-off-by: Philippe Mathieu-Daudé --- net/passt.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/net/passt.c b/net/passt.c index b3d4b71314..4ff94ee509 100644 --- a/net/passt.c +++ b/net/passt.c @@ -270,8 +270,17 @@ static int net_passt_start_daemon(NetPasstState *s, int sock, Error **errp) return -1; } - if (g_subprocess_get_if_exited(daemon) && - g_subprocess_get_exit_status(daemon)) { + if (g_subprocess_get_if_exited(daemon)) { + gint status = g_subprocess_get_exit_status(daemon); + if (status) { + error_setg(errp, "Passt exited with code %d", status); + return -1; + } + } + + if (g_subprocess_get_if_signaled(daemon)) { + error_setg(errp, "Passt killed with signal %d", + g_subprocess_get_term_sig(daemon)); return -1; } From 550391c7134d295d73b2b0e7a1111a922b78c13c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 6 Mar 2026 15:40:16 +0000 Subject: [PATCH 30/49] hw/net/npcm_gmac: Catch accesses off the end of the register array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the npcm_gmac device, we create the iomem MemoryRegion with a size of 8KB, but NPCM_GMAC_NR_REGS is only 0x1060 / 4. This means there's a range of offsets that the guest can access that don't have gmac->regs[] entries. We weren't catching this, so the guest could get us to index off the end of the regs array. Catch and log these invalid accesses. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3316 Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260306154016.2194091-1-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/net/npcm_gmac.c | 14 ++++++++++++++ include/hw/net/npcm_gmac.h | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/hw/net/npcm_gmac.c b/hw/net/npcm_gmac.c index 123fb92ca4..d9902d9ab5 100644 --- a/hw/net/npcm_gmac.c +++ b/hw/net/npcm_gmac.c @@ -700,6 +700,13 @@ static uint64_t npcm_gmac_read(void *opaque, hwaddr offset, unsigned size) NPCMGMACState *gmac = opaque; uint32_t v = 0; + if (offset >= NPCM_GMAC_REG_SIZE) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid register offset: 0x%04" HWADDR_PRIx"\n", + DEVICE(gmac)->canonical_path, offset); + return v; + } + switch (offset) { /* Write only registers */ case A_NPCM_DMA_XMT_POLL_DEMAND: @@ -724,6 +731,13 @@ static void npcm_gmac_write(void *opaque, hwaddr offset, trace_npcm_gmac_reg_write(DEVICE(gmac)->canonical_path, offset, v); + if (offset >= NPCM_GMAC_REG_SIZE) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid register offset: 0x%04" HWADDR_PRIx"\n", + DEVICE(gmac)->canonical_path, offset); + return; + } + switch (offset) { /* Read only registers */ case A_NPCM_GMAC_VERSION: diff --git a/include/hw/net/npcm_gmac.h b/include/hw/net/npcm_gmac.h index d4fe49ada5..23b9841a80 100644 --- a/include/hw/net/npcm_gmac.h +++ b/include/hw/net/npcm_gmac.h @@ -24,7 +24,8 @@ #include "hw/core/sysbus.h" #include "net/net.h" -#define NPCM_GMAC_NR_REGS (0x1060 / sizeof(uint32_t)) +#define NPCM_GMAC_REG_SIZE 0x1060 +#define NPCM_GMAC_NR_REGS (NPCM_GMAC_REG_SIZE / sizeof(uint32_t)) #define NPCM_GMAC_MAX_PHYS 32 #define NPCM_GMAC_MAX_PHY_REGS 32 From cbf8e382039cd17e296f5729f301ad26eb0bd1d5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 7 Mar 2026 11:29:29 +0000 Subject: [PATCH 31/49] hw/sparc/sun4m: Don't set up dummy cpu interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the sun4m machine init, we set up the cpu_irqs[] array with the real inbound IRQs for each CPU, followed by some dummy IRQs for the remaining slots from smp_cpus up to MAX_CPUS. These dummy IRQs do nothing when set/cleared because the dummy_cpu_set_irq() function does nothing. Instead of creating these "do nothing" qemu_irqs, instead pass the number of CPUs to slavio_intctl_init() so that it can only wire up the interrupt controller's interrupts for the CPUs that actually exist. Calling qemu_set_irq() on an irq that isn't connected does nothing, so this is a simpler way to achieve the same result. This cleanup fixes an unimportant memory leak reported by the address sanitizer that happens because we allocate these dummy IRQs with qemu_allocate_irqs(): Direct leak of 1920 byte(s) in 15 object(s) allocated from: #0 0x5cb7b120cf63 in malloc (/home/pm215/qemu/build/san/qemu-system-sparc+0xe0bf63) (BuildId: d27f9230a7cc82ebfaf0cf9e439dc215ddd7ac68) #1 0x743cd6dc5ac9 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62ac9) (BuildId: 116e142b9b52c8a4dfd403e759e71ab8f95d8bb3) #2 0x5cb7b1a42fb4 in qemu_extend_irqs /home/pm215/qemu/build/san/../../hw/core/irq.c:77:51 #3 0x5cb7b19e7e72 in sun4m_hw_init /home/pm215/qemu/build/san/../../hw/sparc/sun4m.c:845:23 #4 0x5cb7b141d3dd in machine_run_board_init /home/pm215/qemu/build/san/../../hw/core/machine.c:1709:5 #5 0x5cb7b1542895 in qemu_init_board /home/pm215/qemu/build/san/../../system/vl.c:2717:5 #6 0x5cb7b1542895 in qmp_x_exit_preconfig /home/pm215/qemu/build/san/../../system/vl.c:2811:5 #7 0x5cb7b15493ac in qemu_init /home/pm215/qemu/build/san/../../system/vl.c:3849:9 #8 0x5cb7b1f3f201 in main /home/pm215/qemu/build/san/../../system/main.c:71:5 #9 0x743cd4a2a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 #10 0x743cd4a2a28a in __libc_start_main csu/../csu/libc-start.c:360:3 #11 0x5cb7b1172114 in _start (/home/pm215/qemu/build/san/qemu-system-sparc+0xd71114) (BuildId: d27f9230a7cc82ebfaf0cf9e439dc215ddd7ac68) Signed-off-by: Peter Maydell Reviewed-by: Mark Cave-Ayland Message-ID: <20260307112931.3322532-2-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/sparc/sun4m.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index a17bdb3692..29bc26ebcb 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -196,10 +196,6 @@ static void cpu_set_irq(void *opaque, int irq, int level) } } -static void dummy_cpu_set_irq(void *opaque, int irq, int level) -{ -} - static void sun4m_cpu_reset(void *opaque) { SPARCCPU *cpu = opaque; @@ -344,6 +340,7 @@ static void *sparc32_dma_init(hwaddr dma_base, static DeviceState *slavio_intctl_init(hwaddr addr, hwaddr addrg, + unsigned int smp_cpus, qemu_irq **parent_irq) { DeviceState *dev; @@ -355,7 +352,7 @@ static DeviceState *slavio_intctl_init(hwaddr addr, s = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(s, &error_fatal); - for (i = 0; i < MAX_CPUS; i++) { + for (i = 0; i < smp_cpus; i++) { for (j = 0; j < MAX_PILS; j++) { sysbus_connect_irq(s, i * MAX_PILS + j, parent_irq[i][j]); } @@ -841,9 +838,6 @@ static void sun4m_hw_init(MachineState *machine) cpu_devinit(machine->cpu_type, i, hwdef->slavio_base, &cpu_irqs[i]); } - for (i = smp_cpus; i < MAX_CPUS; i++) - cpu_irqs[i] = qemu_allocate_irqs(dummy_cpu_set_irq, NULL, MAX_PILS); - /* Create and map RAM frontend */ dev = qdev_new("memory"); object_property_set_link(OBJECT(dev), "memdev", OBJECT(ram_memdev), &error_fatal); @@ -860,6 +854,7 @@ static void sun4m_hw_init(MachineState *machine) slavio_intctl = slavio_intctl_init(hwdef->intctl_base, hwdef->intctl_base + 0x10000ULL, + smp_cpus, cpu_irqs); for (i = 0; i < 32; i++) { From fe00b8eba852e02189626d5cafc489d06ab6a482 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 7 Mar 2026 11:29:30 +0000 Subject: [PATCH 32/49] hw/sparc/sun4m: Use qdev GPIOs rather than qemu_allocate_irqs() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the sun4m machine creation code, we currently use qemu_allocate_irqs() to set up the IRQ lines that act as the inbound IRQ lines to the CPUs. This results in a memory leak: Direct leak of 128 byte(s) in 1 object(s) allocated from: #0 0x5a23c1281ec3 in malloc (/home/pm215/qemu/build/sparc-san/qemu-system-sparc+0xdf1ec3) (BuildId: e6aa10be01feb5524656dd083997bc82b85e3e93) #1 0x79e8f78f0ac9 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62ac9) (BuildId: 116e142b9b52c8a4dfd403e759e71ab8f95d8bb3) #2 0x5a23c1a94e54 in qemu_extend_irqs /home/pm215/qemu/build/sparc-san/../../hw/core/irq.c:77:51 #3 0x5a23c1a39e03 in cpu_devinit /home/pm215/qemu/build/sparc-san/../../hw/sparc/sun4m.c:802:17 #4 0x5a23c1a39e03 in sun4m_hw_init /home/pm215/qemu/build/sparc-san/../../hw/sparc/sun4m.c:838:9 The leak is unimportant as it is a "once at startup" leak, but fixing it helps in getting a clean leak-sanitizer test run. Switch the sun4m code to handle CPU interrupt lines in the same way as the leon3 machine does: the machine init code uses qdev_init_gpio_in to create GPIO lines on the CPU objects. This is a little bit odd as ideally the CPU would do that itself, but for these 32-bit SPARC machines the machine and the CPU are very closely coupled already (the functions handling the IRQ lines modify data fields inside the CPU). Signed-off-by: Peter Maydell Reviewed-by: Mark Cave-Ayland Message-ID: <20260307112931.3322532-3-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/sparc/sun4m.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 29bc26ebcb..b9f8236be5 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -341,7 +341,7 @@ static void *sparc32_dma_init(hwaddr dma_base, static DeviceState *slavio_intctl_init(hwaddr addr, hwaddr addrg, unsigned int smp_cpus, - qemu_irq **parent_irq) + DeviceState **cpus) { DeviceState *dev; SysBusDevice *s; @@ -354,7 +354,8 @@ static DeviceState *slavio_intctl_init(hwaddr addr, for (i = 0; i < smp_cpus; i++) { for (j = 0; j < MAX_PILS; j++) { - sysbus_connect_irq(s, i * MAX_PILS + j, parent_irq[i][j]); + sysbus_connect_irq(s, i * MAX_PILS + j, + qdev_get_gpio_in_named(cpus[i], "pil", j)); } } sysbus_mmio_map(s, 0, addrg); @@ -785,22 +786,25 @@ static const TypeInfo ram_info = { .class_init = ram_class_init, }; -static void cpu_devinit(const char *cpu_type, unsigned int id, - uint64_t prom_addr, qemu_irq **cpu_irqs) +static DeviceState *cpu_devinit(const char *cpu_type, unsigned int id, + uint64_t prom_addr) { SPARCCPU *cpu; CPUSPARCState *env; + DeviceState *cpudev; cpu = SPARC_CPU(object_new(cpu_type)); env = &cpu->env; + cpudev = DEVICE(cpu); qemu_register_reset(sun4m_cpu_reset, cpu); object_property_set_bool(OBJECT(cpu), "start-powered-off", id != 0, &error_abort); - qdev_realize_and_unref(DEVICE(cpu), NULL, &error_fatal); + qdev_init_gpio_in_named(cpudev, cpu_set_irq, "pil", MAX_PILS); + qdev_realize_and_unref(cpudev, NULL, &error_fatal); cpu_sparc_set_id(env, id); - *cpu_irqs = qemu_allocate_irqs(cpu_set_irq, cpu, MAX_PILS); env->prom_addr = prom_addr; + return cpudev; } static void dummy_fdc_tc(void *opaque, int irq, int level) @@ -813,13 +817,14 @@ static void sun4m_hw_init(MachineState *machine) DeviceState *slavio_intctl; unsigned int i; Nvram *nvram; - qemu_irq *cpu_irqs[MAX_CPUS], slavio_irq[32], slavio_cpu_irq[MAX_CPUS]; + qemu_irq slavio_irq[32], slavio_cpu_irq[MAX_CPUS]; qemu_irq fdc_tc; unsigned long kernel_size; uint32_t initrd_size; DriveInfo *fd[MAX_FD]; FWCfgState *fw_cfg; DeviceState *dev, *ms_kb_orgate, *serial_orgate; + DeviceState *cpus[MAX_CPUS]; SysBusDevice *s; unsigned int smp_cpus = machine->smp.cpus; unsigned int max_cpus = machine->smp.max_cpus; @@ -835,7 +840,7 @@ static void sun4m_hw_init(MachineState *machine) /* init CPUs */ for(i = 0; i < smp_cpus; i++) { - cpu_devinit(machine->cpu_type, i, hwdef->slavio_base, &cpu_irqs[i]); + cpus[i] = cpu_devinit(machine->cpu_type, i, hwdef->slavio_base); } /* Create and map RAM frontend */ @@ -855,7 +860,7 @@ static void sun4m_hw_init(MachineState *machine) slavio_intctl = slavio_intctl_init(hwdef->intctl_base, hwdef->intctl_base + 0x10000ULL, smp_cpus, - cpu_irqs); + cpus); for (i = 0; i < 32; i++) { slavio_irq[i] = qdev_get_gpio_in(slavio_intctl, i); From c9949c096b2b89a1b118bf483e575ae9c403deb9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 7 Mar 2026 11:29:31 +0000 Subject: [PATCH 33/49] hw/timer/slavio_timer: Free resources allocated in instance_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The slavio_timer device's instance_init function allocates memory for TimerContext structs and a ptimer, but it never frees this memory, so we will leak it if the QMP interface does introspection of this device type, as reported by the clang address sanitizer: Indirect leak of 4896 byte(s) in 17 object(s) allocated from: #0 0x5f2948d9b14d in calloc (/home/pm215/qemu/build/san/qemu-system-sparc+0xe0c14d) (BuildId: 7210711bdf6f7fbd0b863bd2dfcc7c42c7175db1) #1 0x758584b11771 in g_malloc0 (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x63771) (BuildId: 116e142b9b52c8a4dfd403e759e71ab8f95d8bb3) #2 0x5f2949097b8a in slavio_timer_init /home/pm215/qemu/build/san/../../hw/timer/slavio_timer.c:403:14 #3 0x5f29495d790f in object_initialize_with_type /home/pm215/qemu/build/san/../../qom/object.c:570:5 #4 0x5f29495d96ef in object_new_with_type /home/pm215/qemu/build/san/../../qom/object.c:774:5 #5 0x5f2949a30a26 in qmp_device_list_properties /home/pm215/qemu/build/san/../../qom/qom-qmp-cmds.c:206:11 Indirect leak of 1632 byte(s) in 17 object(s) allocated from: #0 0x5f2948d9b14d in calloc (/home/pm215/qemu/build/san/qemu-system-sparc+0xe0c14d) (BuildId: 7210711bdf6f7fbd0b863bd2dfcc7c42c7175db1) #1 0x758584b11771 in g_malloc0 (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x63771) (BuildId: 116e142b9b52c8a4dfd403e759e71ab8f95d8bb3) #2 0x5f2948f7c65a in ptimer_init /home/pm215/qemu/build/san/../../hw/core/ptimer.c:464:9 #3 0x5f2949097c1f in slavio_timer_init /home/pm215/qemu/build/san/../../hw/timer/slavio_timer.c:407:32 #4 0x5f29495d790f in object_initialize_with_type /home/pm215/qemu/build/san/../../qom/object.c:570:5 #5 0x5f29495d96ef in object_new_with_type /home/pm215/qemu/build/san/../../qom/object.c:774:5 #6 0x5f2949a30a26 in qmp_device_list_properties /home/pm215/qemu/build/san/../../qom/qom-qmp-cmds.c:206:11 Avoid the TimerContext leaks by making them an array inside the SLAVIO_TimerState struct instead of allocating a compile-time-fixed number of them each individually with g_new0() and then throwing away the pointer. Avoid the ptimer() leak by calling ptimer_free in instance_finalize(). Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Mark Cave-Ayland Message-ID: <20260307112931.3322532-4-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/timer/slavio_timer.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/hw/timer/slavio_timer.c b/hw/timer/slavio_timer.c index 4a3e227fba..eccdc532fb 100644 --- a/hw/timer/slavio_timer.c +++ b/hw/timer/slavio_timer.c @@ -62,20 +62,21 @@ typedef struct CPUTimerState { #define TYPE_SLAVIO_TIMER "slavio_timer" OBJECT_DECLARE_SIMPLE_TYPE(SLAVIO_TIMERState, SLAVIO_TIMER) +typedef struct TimerContext { + MemoryRegion iomem; + SLAVIO_TIMERState *s; + unsigned int timer_index; /* 0 for system, 1 ... MAX_CPUS for CPU timers */ +} TimerContext; + struct SLAVIO_TIMERState { SysBusDevice parent_obj; uint32_t num_cpus; uint32_t cputimer_mode; CPUTimerState cputimer[MAX_CPUS + 1]; + TimerContext timer_context[MAX_CPUS + 1]; }; -typedef struct TimerContext { - MemoryRegion iomem; - SLAVIO_TIMERState *s; - unsigned int timer_index; /* 0 for system, 1 ... MAX_CPUS for CPU timers */ -} TimerContext; - #define SYS_TIMER_SIZE 0x14 #define CPU_TIMER_SIZE 0x10 @@ -400,7 +401,7 @@ static void slavio_timer_init(Object *obj) uint64_t size; char timer_name[20]; - tc = g_new0(TimerContext, 1); + tc = &s->timer_context[i]; tc->s = s; tc->timer_index = i; @@ -420,6 +421,15 @@ static void slavio_timer_init(Object *obj) } } +static void slavio_timer_finalize(Object *obj) +{ + SLAVIO_TIMERState *s = SLAVIO_TIMER(obj); + + for (int i = 0; i <= MAX_CPUS; i++) { + ptimer_free(s->cputimer[i].timer); + } +} + static const Property slavio_timer_properties[] = { DEFINE_PROP_UINT32("num_cpus", SLAVIO_TIMERState, num_cpus, 0), }; @@ -438,6 +448,7 @@ static const TypeInfo slavio_timer_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SLAVIO_TIMERState), .instance_init = slavio_timer_init, + .instance_finalize = slavio_timer_finalize, .class_init = slavio_timer_class_init, }; From 7f2cef7f559601f20b675a8532ec96d0e7354061 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 7 Mar 2026 15:50:45 +0000 Subject: [PATCH 34/49] hw/qdev: Document qdev_get_dev_path() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't document qdev_get_dev_path() at all; in particular we do not mention that it returns a string that the caller must free. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260307155046.3940197-2-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- include/hw/core/qdev.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/include/hw/core/qdev.h b/include/hw/core/qdev.h index 4b2730e9d8..29083bc907 100644 --- a/include/hw/core/qdev.h +++ b/include/hw/core/qdev.h @@ -324,6 +324,10 @@ struct BusClass { /* FIXME first arg should be BusState */ void (*print_dev)(Monitor *mon, DeviceState *dev, int indent); + /* + * Return a newly allocated string containing the path of the + * device on this bus. + */ char *(*get_dev_path)(DeviceState *dev); /* @@ -1060,6 +1064,25 @@ bool qdev_set_parent_bus(DeviceState *dev, BusState *bus, Error **errp); extern bool qdev_hot_removed; +/** + * qdev_get_dev_path(): Return the path of a device on its bus + * @dev: device to get the path of + * + * Returns: A newly allocated string containing the dev path of + * @dev. The caller must free this with g_free(). + * The format of the string depends on the bus; for instance a + * PCI device's path will be in the format:: + * + * Domain:00:Slot.Function:Slot.Function....:Slot.Function + * + * and a SCSI device's path will be:: + * + * channel:ID:LUN + * + * (possibly prefixed by the path of the SCSI controller). + * + * If @dev is NULL or not on a bus, returns NULL. + */ char *qdev_get_dev_path(DeviceState *dev); const char *qdev_get_printable_name(DeviceState *dev); From 1e3e1d51e20e8b38efa089bf54b5ee2cbbcca221 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 7 Mar 2026 15:50:46 +0000 Subject: [PATCH 35/49] hw: Make qdev_get_printable_name() consistently return freeable string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current implementation of qdev_get_printable_name() sometimes returns a string that must not be freed (vdev->id or the fixed fallback string "" and sometimes returns a string that must be freed (the return value of qdev_get_dev_path()). This forces callers to leak the string in the "must be freed" case. Make the function consistent that it always returns a string that the caller must free, and make the three callsites free it. This fixes leaks like this that show up when running "make check" with the address sanitizer enabled: Direct leak of 13 byte(s) in 1 object(s) allocated from: #0 0x5561de21f293 in malloc (/home/pm215/qemu/build/san/qemu-system-i386+0x1a2d293) (BuildId: 6d6fad7130fd5c8dbbc03401df554f68b8034936) #1 0x767ad7a82ac9 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62ac9) (BuildId: 116e142b9b52c8a4dfd403e759e71ab8f95d8bb3) #2 0x5561deaf34f2 in pcibus_get_dev_path /home/pm215/qemu/build/san/../../hw/pci/pci.c:2792:12 #3 0x5561df9d8830 in qdev_get_printable_name /home/pm215/qemu/build/san/../../hw/core/qdev.c:431:24 #4 0x5561deebdca2 in virtio_init_region_cache /home/pm215/qemu/build/san/../../hw/virtio/virtio.c:298:17 #5 0x5561df05f842 in memory_region_write_accessor /home/pm215/qemu/build/san/../../system/memory.c:491:5 #6 0x5561df05ed1b in access_with_adjusted_size /home/pm215/qemu/build/san/../../system/memory.c:567:18 #7 0x5561df05e3fa in memory_region_dispatch_write /home/pm215/qemu/build/san/../../system/memory.c #8 0x5561df0aa805 in address_space_stm_internal /home/pm215/qemu/build/san/../../system/memory_ldst.c.inc:85:13 #9 0x5561df0bcad3 in qtest_process_command /home/pm215/qemu/build/san/../../system/qtest.c:480:13 Cc: qemu-stable@nongnu.org Fixes: e209d4d7a31b9 ("virtio: improve virtqueue mapping error messages") Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260307155046.3940197-3-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/qdev.c | 4 ++-- hw/virtio/virtio.c | 12 +++++++++--- include/hw/core/qdev.h | 16 ++++++++++++++++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/hw/core/qdev.c b/hw/core/qdev.c index fc3425a8fe..e48616b2c6 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -420,7 +420,7 @@ const char *qdev_get_printable_name(DeviceState *vdev) * names. */ if (vdev->id) { - return vdev->id; + return g_strdup(vdev->id); } /* * Fall back to the canonical QOM device path (eg. ID for PCI @@ -437,7 +437,7 @@ const char *qdev_get_printable_name(DeviceState *vdev) * Final fallback: if all else fails, return a placeholder string. * This ensures the error message always contains a valid string. */ - return ""; + return g_strdup(""); } void qdev_add_unplug_blocker(DeviceState *dev, Error *reason) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 4c5a4479bf..0ba734d0bc 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -281,10 +281,12 @@ void virtio_init_region_cache(VirtIODevice *vdev, int n) len = address_space_cache_init(&new->desc, vdev->dma_as, addr, size, packed); if (len < size) { + g_autofree const char *devname = qdev_get_printable_name(DEVICE(vdev)); + virtio_error(vdev, "Failed to map descriptor ring for device %s: " "invalid guest physical address or corrupted queue setup", - qdev_get_printable_name(DEVICE(vdev))); + devname); goto err_desc; } @@ -292,10 +294,12 @@ void virtio_init_region_cache(VirtIODevice *vdev, int n) len = address_space_cache_init(&new->used, vdev->dma_as, vq->vring.used, size, true); if (len < size) { + g_autofree const char *devname = qdev_get_printable_name(DEVICE(vdev)); + virtio_error(vdev, "Failed to map used ring for device %s: " "possible guest misconfiguration or insufficient memory", - qdev_get_printable_name(DEVICE(vdev))); + devname); goto err_used; } @@ -303,10 +307,12 @@ void virtio_init_region_cache(VirtIODevice *vdev, int n) len = address_space_cache_init(&new->avail, vdev->dma_as, vq->vring.avail, size, false); if (len < size) { + g_autofree const char *devname = qdev_get_printable_name(DEVICE(vdev)); + virtio_error(vdev, "Failed to map avalaible ring for device %s: " "possible queue misconfiguration or overlapping memory region", - qdev_get_printable_name(DEVICE(vdev))); + devname); goto err_avail; } diff --git a/include/hw/core/qdev.h b/include/hw/core/qdev.h index 29083bc907..f99a8979cc 100644 --- a/include/hw/core/qdev.h +++ b/include/hw/core/qdev.h @@ -1084,6 +1084,22 @@ extern bool qdev_hot_removed; * If @dev is NULL or not on a bus, returns NULL. */ char *qdev_get_dev_path(DeviceState *dev); + +/** + * qdev_get_printable_name: Return human readable name for device + * @dev: Device to get name of + * + * Returns: A newly allocated string containing some human + * readable name for the device, suitable for printing in + * user-facing error messages. The function will never return NULL, + * so the name can be used without further checking or fallbacks. + * + * If the device has an explicitly set ID (e.g. by the user on the + * command line via "-device thisdev,id=myid") this is preferred. + * Otherwise we try the canonical QOM device path (which will be + * the PCI ID for PCI devices, for example). If all else fails + * we will return the placeholder ". + */ const char *qdev_get_printable_name(DeviceState *dev); void qbus_set_hotplug_handler(BusState *bus, Object *handler); From 82395173935a15868561e604ffd1d4a60eec4689 Mon Sep 17 00:00:00 2001 From: Chad Jablonski Date: Mon, 9 Mar 2026 02:47:44 +0100 Subject: [PATCH 36/49] ati-vga: Remove src and dst stride mutation in ati_2d_blt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pixman requires stride in words. So over the course of the ati_2d_blt function both src and dst stride were mutated before being passed to pixman and then back afterwards. This creates local variables holding src and dst stride in words avoiding the potentially confusing mutation. Signed-off-by: Chad Jablonski Reviewed-by: BALATON Zoltan [balaton: Fix build without pixman] Signed-off-by: BALATON Zoltan Message-ID: <7ab2227b96b73a63cb4fc61d0e6921e01900a4ee.1773020351.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/ati_2d.c | 48 +++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c index 8e192802d3..a5b475ee18 100644 --- a/hw/display/ati_2d.c +++ b/hw/display/ati_2d.c @@ -126,34 +126,34 @@ void ati_2d_blt(ATIVGAState *s) return; } - src_stride /= sizeof(uint32_t); - dst_stride /= sizeof(uint32_t); - DPRINTF("pixman_blt(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d)\n", - src_bits, dst_bits, src_stride, dst_stride, bpp, bpp, - src_x, src_y, dst_x, dst_y, - s->regs.dst_width, s->regs.dst_height); + DPRINTF("pixman_blt(%p, %p, %ld, %ld, %d, %d, %d, %d, %d, %d, %d, %d)\n", + src_bits, dst_bits, src_stride / sizeof(uint32_t), + dst_stride / sizeof(uint32_t), bpp, bpp, src_x, src_y, dst_x, + dst_y, s->regs.dst_width, s->regs.dst_height); #ifdef CONFIG_PIXMAN + int src_stride_words = src_stride / sizeof(uint32_t); + int dst_stride_words = dst_stride / sizeof(uint32_t); if ((s->use_pixman & BIT(1)) && s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT && s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM) { fallback = !pixman_blt((uint32_t *)src_bits, (uint32_t *)dst_bits, - src_stride, dst_stride, bpp, bpp, + src_stride_words, dst_stride_words, bpp, bpp, src_x, src_y, dst_x, dst_y, s->regs.dst_width, s->regs.dst_height); } else if (s->use_pixman & BIT(1)) { /* FIXME: We only really need a temporary if src and dst overlap */ int llb = s->regs.dst_width * (bpp / 8); - int tmp_stride = DIV_ROUND_UP(llb, sizeof(uint32_t)); - uint32_t *tmp = g_malloc(tmp_stride * sizeof(uint32_t) * + int tmp_stride_words = DIV_ROUND_UP(llb, sizeof(uint32_t)); + uint32_t *tmp = g_malloc(tmp_stride_words * sizeof(uint32_t) * s->regs.dst_height); fallback = !pixman_blt((uint32_t *)src_bits, tmp, - src_stride, tmp_stride, bpp, bpp, + src_stride_words, tmp_stride_words, bpp, bpp, src_x, src_y, 0, 0, s->regs.dst_width, s->regs.dst_height); if (!fallback) { fallback = !pixman_blt(tmp, (uint32_t *)dst_bits, - tmp_stride, dst_stride, bpp, bpp, - 0, 0, dst_x, dst_y, + tmp_stride_words, dst_stride_words, + bpp, bpp, 0, 0, dst_x, dst_y, s->regs.dst_width, s->regs.dst_height); } g_free(tmp); @@ -164,18 +164,15 @@ void ati_2d_blt(ATIVGAState *s) } if (fallback) { unsigned int y, i, j, bypp = bpp / 8; - unsigned int src_pitch = src_stride * sizeof(uint32_t); - unsigned int dst_pitch = dst_stride * sizeof(uint32_t); - for (y = 0; y < s->regs.dst_height; y++) { i = dst_x * bypp; j = src_x * bypp; if (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM) { - i += (dst_y + y) * dst_pitch; - j += (src_y + y) * src_pitch; + i += (dst_y + y) * dst_stride; + j += (src_y + y) * src_stride; } else { - i += (dst_y + s->regs.dst_height - 1 - y) * dst_pitch; - j += (src_y + s->regs.dst_height - 1 - y) * src_pitch; + i += (dst_y + s->regs.dst_height - 1 - y) * dst_stride; + j += (src_y + s->regs.dst_height - 1 - y) * src_stride; } memmove(&dst_bits[i], &src_bits[j], s->regs.dst_width * bypp); } @@ -202,21 +199,20 @@ void ati_2d_blt(ATIVGAState *s) break; } - dst_stride /= sizeof(uint32_t); - DPRINTF("pixman_fill(%p, %d, %d, %d, %d, %d, %d, %x)\n", - dst_bits, dst_stride, bpp, dst_x, dst_y, + DPRINTF("pixman_fill(%p, %ld, %d, %d, %d, %d, %d, %x)\n", + dst_bits, dst_stride / sizeof(uint32_t), bpp, dst_x, dst_y, s->regs.dst_width, s->regs.dst_height, filler); #ifdef CONFIG_PIXMAN if (!(s->use_pixman & BIT(0)) || - !pixman_fill((uint32_t *)dst_bits, dst_stride, bpp, dst_x, dst_y, - s->regs.dst_width, s->regs.dst_height, filler)) + !pixman_fill((uint32_t *)dst_bits, dst_stride / sizeof(uint32_t), + bpp, dst_x, dst_y, s->regs.dst_width, + s->regs.dst_height, filler)) #endif { /* fallback when pixman failed or we don't want to call it */ unsigned int x, y, i, bypp = bpp / 8; - unsigned int dst_pitch = dst_stride * sizeof(uint32_t); for (y = 0; y < s->regs.dst_height; y++) { - i = dst_x * bypp + (dst_y + y) * dst_pitch; + i = dst_x * bypp + (dst_y + y) * dst_stride; for (x = 0; x < s->regs.dst_width; x++, i += bypp) { stn_he_p(&dst_bits[i], bypp, filler); } From 67f074bf24ea0426facb06827a76cac2850757e9 Mon Sep 17 00:00:00 2001 From: Chad Jablonski Date: Mon, 9 Mar 2026 02:47:45 +0100 Subject: [PATCH 37/49] ati-vga: Use local variables for register values in ati_2d_blt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ati_2d_blt uses a mixture of locals and direct register access of needed state. This assigns all values derived from register state to local variables. It prepares the function for a larger refactor that removes the dependency on the full device and direct register access entirely. Signed-off-by: Chad Jablonski Reviewed-by: BALATON Zoltan [balaton: Fix build without pixman] Signed-off-by: BALATON Zoltan Message-ID: <2c7f8fdc527e8c5bad237933a1c8a7a8e5068a3d.1773020351.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/ati_2d.c | 92 +++++++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 44 deletions(-) diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c index a5b475ee18..cdca751863 100644 --- a/hw/display/ati_2d.c +++ b/hw/display/ati_2d.c @@ -66,10 +66,18 @@ void ati_2d_blt(ATIVGAState *s) { /* FIXME it is probably more complex than this and may need to be */ /* rewritten but for now as a start just to get some output: */ - unsigned dst_x = (s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT ? - s->regs.dst_x : s->regs.dst_x + 1 - s->regs.dst_width); - unsigned dst_y = (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM ? - s->regs.dst_y : s->regs.dst_y + 1 - s->regs.dst_height); + uint32_t rop3 = s->regs.dp_mix & GMC_ROP3_MASK; + bool left_to_right = s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT; + bool top_to_bottom = s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM; + uint32_t frgd_clr = s->regs.dp_brush_frgd_clr; + uint8_t *palette = s->vga.palette; + unsigned dst_offset = s->regs.dst_offset; + unsigned dst_width = s->regs.dst_width; + unsigned dst_height = s->regs.dst_height; + unsigned dst_x = (left_to_right ? + s->regs.dst_x : s->regs.dst_x + 1 - dst_width); + unsigned dst_y = (top_to_bottom ? + s->regs.dst_y : s->regs.dst_y + 1 - dst_height); int bpp = ati_bpp_from_datatype(s); if (!bpp) { qemu_log_mask(LOG_GUEST_ERROR, "Invalid bpp\n"); @@ -80,7 +88,7 @@ void ati_2d_blt(ATIVGAState *s) qemu_log_mask(LOG_GUEST_ERROR, "Zero dest pitch\n"); return; } - uint8_t *dst_bits = s->vga.vram_ptr + s->regs.dst_offset; + uint8_t *dst_bits = s->vga.vram_ptr + dst_offset; if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) { dst_bits += s->regs.crtc_offset & 0x07ffffff; @@ -88,26 +96,25 @@ void ati_2d_blt(ATIVGAState *s) } uint8_t *end = s->vga.vram_ptr + s->vga.vram_size; if (dst_x > 0x3fff || dst_y > 0x3fff || dst_bits >= end - || dst_bits + dst_x - + (dst_y + s->regs.dst_height) * dst_stride >= end) { + || dst_bits + dst_x + (dst_y + dst_height) * dst_stride >= end) { qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n"); return; } DPRINTF("%d %d %d, %d %d %d, (%d,%d) -> (%d,%d) %dx%d %c %c\n", - s->regs.src_offset, s->regs.dst_offset, s->regs.default_offset, - s->regs.src_pitch, s->regs.dst_pitch, s->regs.default_pitch, + s->regs.src_offset, dst_offset, s->regs.default_offset, + s->regs.src_pitch, dst_stride, s->regs.default_pitch, s->regs.src_x, s->regs.src_y, dst_x, dst_y, - s->regs.dst_width, s->regs.dst_height, - (s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT ? '>' : '<'), - (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM ? 'v' : '^')); - switch (s->regs.dp_mix & GMC_ROP3_MASK) { + dst_width, dst_height, + (left_to_right ? '>' : '<'), + (top_to_bottom ? 'v' : '^')); + switch (rop3) { case ROP3_SRCCOPY: { bool fallback = false; - unsigned src_x = (s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT ? - s->regs.src_x : s->regs.src_x + 1 - s->regs.dst_width); - unsigned src_y = (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM ? - s->regs.src_y : s->regs.src_y + 1 - s->regs.dst_height); + unsigned src_x = (left_to_right ? + s->regs.src_x : s->regs.src_x + 1 - dst_width); + unsigned src_y = (top_to_bottom ? + s->regs.src_y : s->regs.src_y + 1 - dst_height); int src_stride = s->regs.src_pitch; if (!src_stride) { qemu_log_mask(LOG_GUEST_ERROR, "Zero source pitch\n"); @@ -121,7 +128,7 @@ void ati_2d_blt(ATIVGAState *s) } if (src_x > 0x3fff || src_y > 0x3fff || src_bits >= end || src_bits + src_x - + (src_y + s->regs.dst_height) * src_stride >= end) { + + (src_y + dst_height) * src_stride >= end) { qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n"); return; } @@ -129,32 +136,30 @@ void ati_2d_blt(ATIVGAState *s) DPRINTF("pixman_blt(%p, %p, %ld, %ld, %d, %d, %d, %d, %d, %d, %d, %d)\n", src_bits, dst_bits, src_stride / sizeof(uint32_t), dst_stride / sizeof(uint32_t), bpp, bpp, src_x, src_y, dst_x, - dst_y, s->regs.dst_width, s->regs.dst_height); + dst_y, dst_width, dst_height); #ifdef CONFIG_PIXMAN int src_stride_words = src_stride / sizeof(uint32_t); int dst_stride_words = dst_stride / sizeof(uint32_t); - if ((s->use_pixman & BIT(1)) && - s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT && - s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM) { + if ((s->use_pixman & BIT(1)) && left_to_right && top_to_bottom) { fallback = !pixman_blt((uint32_t *)src_bits, (uint32_t *)dst_bits, src_stride_words, dst_stride_words, bpp, bpp, src_x, src_y, dst_x, dst_y, - s->regs.dst_width, s->regs.dst_height); + dst_width, dst_height); } else if (s->use_pixman & BIT(1)) { /* FIXME: We only really need a temporary if src and dst overlap */ - int llb = s->regs.dst_width * (bpp / 8); + int llb = dst_width * (bpp / 8); int tmp_stride_words = DIV_ROUND_UP(llb, sizeof(uint32_t)); uint32_t *tmp = g_malloc(tmp_stride_words * sizeof(uint32_t) * - s->regs.dst_height); + dst_height); fallback = !pixman_blt((uint32_t *)src_bits, tmp, src_stride_words, tmp_stride_words, bpp, bpp, src_x, src_y, 0, 0, - s->regs.dst_width, s->regs.dst_height); + dst_width, dst_height); if (!fallback) { fallback = !pixman_blt(tmp, (uint32_t *)dst_bits, tmp_stride_words, dst_stride_words, bpp, bpp, 0, 0, dst_x, dst_y, - s->regs.dst_width, s->regs.dst_height); + dst_width, dst_height); } g_free(tmp); } else @@ -164,17 +169,17 @@ void ati_2d_blt(ATIVGAState *s) } if (fallback) { unsigned int y, i, j, bypp = bpp / 8; - for (y = 0; y < s->regs.dst_height; y++) { + for (y = 0; y < dst_height; y++) { i = dst_x * bypp; j = src_x * bypp; - if (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM) { + if (top_to_bottom) { i += (dst_y + y) * dst_stride; j += (src_y + y) * src_stride; } else { - i += (dst_y + s->regs.dst_height - 1 - y) * dst_stride; - j += (src_y + s->regs.dst_height - 1 - y) * src_stride; + i += (dst_y + dst_height - 1 - y) * dst_stride; + j += (src_y + dst_height - 1 - y) * src_stride; } - memmove(&dst_bits[i], &src_bits[j], s->regs.dst_width * bypp); + memmove(&dst_bits[i], &src_bits[j], dst_width * bypp); } } break; @@ -185,35 +190,34 @@ void ati_2d_blt(ATIVGAState *s) { uint32_t filler = 0; - switch (s->regs.dp_mix & GMC_ROP3_MASK) { + switch (rop3) { case ROP3_PATCOPY: - filler = s->regs.dp_brush_frgd_clr; + filler = frgd_clr; break; case ROP3_BLACKNESS: - filler = 0xffUL << 24 | rgb_to_pixel32(s->vga.palette[0], - s->vga.palette[1], s->vga.palette[2]); + filler = 0xffUL << 24 | rgb_to_pixel32(palette[0], palette[1], + palette[2]); break; case ROP3_WHITENESS: - filler = 0xffUL << 24 | rgb_to_pixel32(s->vga.palette[3], - s->vga.palette[4], s->vga.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", dst_bits, dst_stride / sizeof(uint32_t), bpp, dst_x, dst_y, - s->regs.dst_width, s->regs.dst_height, filler); + dst_width, dst_height, filler); #ifdef CONFIG_PIXMAN if (!(s->use_pixman & BIT(0)) || !pixman_fill((uint32_t *)dst_bits, dst_stride / sizeof(uint32_t), - bpp, dst_x, dst_y, s->regs.dst_width, - s->regs.dst_height, filler)) + bpp, dst_x, dst_y, dst_width, dst_height, filler)) #endif { /* fallback when pixman failed or we don't want to call it */ unsigned int x, y, i, bypp = bpp / 8; - for (y = 0; y < s->regs.dst_height; y++) { + for (y = 0; y < dst_height; y++) { i = dst_x * bypp + (dst_y + y) * dst_stride; - for (x = 0; x < s->regs.dst_width; x++, i += bypp) { + for (x = 0; x < dst_width; x++, i += bypp) { stn_he_p(&dst_bits[i], bypp, filler); } } @@ -222,7 +226,7 @@ void ati_2d_blt(ATIVGAState *s) } default: qemu_log_mask(LOG_UNIMP, "Unimplemented ati_2d blt op %x\n", - (s->regs.dp_mix & GMC_ROP3_MASK) >> 16); + rop3 >> 16); return; } From 6df405b1cf03f80b1fb0c821bbd846c857a75f0a Mon Sep 17 00:00:00 2001 From: Chad Jablonski Date: Mon, 9 Mar 2026 14:09:26 +0100 Subject: [PATCH 38/49] ati-vga: Introduce ATI2DCtx struct for 2D blit context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously all state derived from registers was moved to locals. Now we can mechanically replace those locals with fields on the new ATI2DCtx struct. Signed-off-by: Chad Jablonski Reviewed-by: BALATON Zoltan [balaton: Fix build without pixman] Signed-off-by: BALATON Zoltan Message-ID: <20260309130926.942145969E5@zero.eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/ati_2d.c | 229 +++++++++++++++++++++++++------------------- 1 file changed, 132 insertions(+), 97 deletions(-) diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c index cdca751863..1ae2fbc5f8 100644 --- a/hw/display/ati_2d.c +++ b/hw/display/ati_2d.c @@ -13,6 +13,7 @@ #include "qemu/log.h" #include "ui/pixel_ops.h" #include "ui/console.h" +#include "ui/rect.h" /* * NOTE: @@ -43,22 +44,39 @@ static int ati_bpp_from_datatype(ATIVGAState *s) } } -static void ati_set_dirty(ATIVGAState *s, - const uint8_t *dst_bits, unsigned dst_y) +typedef struct { + int bpp; + uint32_t rop3; + bool left_to_right; + bool top_to_bottom; + uint32_t frgd_clr; + const uint8_t *palette; + const uint8_t *vram_end; + + QemuRect dst; + int dst_stride; + uint8_t *dst_bits; + uint32_t dst_offset; + + QemuRect src; + int src_stride; + const uint8_t *src_bits; +} ATI2DCtx; + +static void ati_set_dirty(VGACommonState *vga, const ATI2DCtx *ctx) { - VGACommonState *vga = &s->vga; DisplaySurface *ds = qemu_console_surface(vga->con); DPRINTF("%p %u ds: %p %d %d rop: %x\n", vga->vram_ptr, vga->vbe_start_addr, surface_data(ds), surface_stride(ds), surface_bits_per_pixel(ds), - (s->regs.dp_mix & GMC_ROP3_MASK) >> 16); - if (dst_bits >= vga->vram_ptr + vga->vbe_start_addr && - dst_bits < vga->vram_ptr + vga->vbe_start_addr + - vga->vbe_regs[VBE_DISPI_INDEX_YRES] * vga->vbe_line_offset) { + ctx->rop3 >> 16); + if (ctx->dst_bits >= vga->vram_ptr + vga->vbe_start_addr && + ctx->dst_bits < vga->vram_ptr + vga->vbe_start_addr + + vga->vbe_regs[VBE_DISPI_INDEX_YRES] * vga->vbe_line_offset) { memory_region_set_dirty(&vga->vram, - vga->vbe_start_addr + s->regs.dst_offset - + dst_y * surface_stride(ds), - s->regs.dst_height * surface_stride(ds)); + vga->vbe_start_addr + ctx->dst_offset + + ctx->dst.y * surface_stride(ds), + ctx->dst.height * surface_stride(ds)); } } @@ -66,100 +84,109 @@ void ati_2d_blt(ATIVGAState *s) { /* FIXME it is probably more complex than this and may need to be */ /* rewritten but for now as a start just to get some output: */ - uint32_t rop3 = s->regs.dp_mix & GMC_ROP3_MASK; - bool left_to_right = s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT; - bool top_to_bottom = s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM; - uint32_t frgd_clr = s->regs.dp_brush_frgd_clr; - uint8_t *palette = s->vga.palette; - unsigned dst_offset = s->regs.dst_offset; - unsigned dst_width = s->regs.dst_width; - unsigned dst_height = s->regs.dst_height; - unsigned dst_x = (left_to_right ? - s->regs.dst_x : s->regs.dst_x + 1 - dst_width); - unsigned dst_y = (top_to_bottom ? - s->regs.dst_y : s->regs.dst_y + 1 - dst_height); - int bpp = ati_bpp_from_datatype(s); - if (!bpp) { + ATI2DCtx ctx_; + ATI2DCtx *ctx = &ctx_; + ctx->rop3 = s->regs.dp_mix & GMC_ROP3_MASK; + ctx->left_to_right = s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT; + ctx->top_to_bottom = s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM; + ctx->frgd_clr = s->regs.dp_brush_frgd_clr; + ctx->palette = s->vga.palette; + ctx->dst_offset = s->regs.dst_offset; + ctx->dst.width = s->regs.dst_width; + ctx->dst.height = s->regs.dst_height; + ctx->dst.x = (ctx->left_to_right ? + s->regs.dst_x : s->regs.dst_x + 1 - ctx->dst.width); + ctx->dst.y = (ctx->top_to_bottom ? + s->regs.dst_y : s->regs.dst_y + 1 - ctx->dst.height); + ctx->bpp = ati_bpp_from_datatype(s); + if (!ctx->bpp) { qemu_log_mask(LOG_GUEST_ERROR, "Invalid bpp\n"); return; } - int dst_stride = s->regs.dst_pitch; - if (!dst_stride) { + ctx->dst_stride = s->regs.dst_pitch; + if (!ctx->dst_stride) { qemu_log_mask(LOG_GUEST_ERROR, "Zero dest pitch\n"); return; } - uint8_t *dst_bits = s->vga.vram_ptr + dst_offset; + ctx->dst_bits = s->vga.vram_ptr + ctx->dst_offset; if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) { - dst_bits += s->regs.crtc_offset & 0x07ffffff; - dst_stride *= bpp; + ctx->dst_bits += s->regs.crtc_offset & 0x07ffffff; + ctx->dst_stride *= ctx->bpp; } - uint8_t *end = s->vga.vram_ptr + s->vga.vram_size; - if (dst_x > 0x3fff || dst_y > 0x3fff || dst_bits >= end - || dst_bits + dst_x + (dst_y + dst_height) * dst_stride >= end) { + ctx->vram_end = s->vga.vram_ptr + s->vga.vram_size; + if (ctx->dst.x > 0x3fff || ctx->dst.y > 0x3fff || + ctx->dst_bits >= ctx->vram_end || ctx->dst_bits + ctx->dst.x + + (ctx->dst.y + ctx->dst.height) * ctx->dst_stride >= ctx->vram_end) { qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n"); return; } DPRINTF("%d %d %d, %d %d %d, (%d,%d) -> (%d,%d) %dx%d %c %c\n", - s->regs.src_offset, dst_offset, s->regs.default_offset, - s->regs.src_pitch, dst_stride, s->regs.default_pitch, - s->regs.src_x, s->regs.src_y, dst_x, dst_y, - dst_width, dst_height, - (left_to_right ? '>' : '<'), - (top_to_bottom ? 'v' : '^')); - switch (rop3) { + s->regs.src_offset, ctx->dst_offset, s->regs.default_offset, + ctx->src_stride, ctx->dst_stride, s->regs.default_pitch, + ctx->src.x, ctx->src.y, ctx->dst.x, ctx->dst.y, + ctx->dst.width, ctx->dst.height, + (ctx->left_to_right ? '>' : '<'), + (ctx->top_to_bottom ? 'v' : '^')); + switch (ctx->rop3) { case ROP3_SRCCOPY: { bool fallback = false; - unsigned src_x = (left_to_right ? - s->regs.src_x : s->regs.src_x + 1 - dst_width); - unsigned src_y = (top_to_bottom ? - s->regs.src_y : s->regs.src_y + 1 - dst_height); - int src_stride = s->regs.src_pitch; - if (!src_stride) { + ctx->src.x = (ctx->left_to_right ? + s->regs.src_x : s->regs.src_x + 1 - ctx->dst.width); + ctx->src.y = (ctx->top_to_bottom ? + s->regs.src_y : s->regs.src_y + 1 - ctx->dst.height); + ctx->src_stride = s->regs.src_pitch; + if (!ctx->src_stride) { qemu_log_mask(LOG_GUEST_ERROR, "Zero source pitch\n"); return; } - uint8_t *src_bits = s->vga.vram_ptr + s->regs.src_offset; + ctx->src_bits = s->vga.vram_ptr + s->regs.src_offset; if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) { - src_bits += s->regs.crtc_offset & 0x07ffffff; - src_stride *= bpp; + ctx->src_bits += s->regs.crtc_offset & 0x07ffffff; + ctx->src_stride *= ctx->bpp; } - if (src_x > 0x3fff || src_y > 0x3fff || src_bits >= end - || src_bits + src_x - + (src_y + dst_height) * src_stride >= end) { + if (ctx->src.x > 0x3fff || ctx->src.y > 0x3fff || + ctx->src_bits >= ctx->vram_end || + ctx->src_bits + ctx->src.x + (ctx->src.y + ctx->dst.height) * + ctx->src_stride >= ctx->vram_end) { qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n"); return; } DPRINTF("pixman_blt(%p, %p, %ld, %ld, %d, %d, %d, %d, %d, %d, %d, %d)\n", - src_bits, dst_bits, src_stride / sizeof(uint32_t), - dst_stride / sizeof(uint32_t), bpp, bpp, src_x, src_y, dst_x, - dst_y, dst_width, dst_height); + ctx->src_bits, ctx->dst_bits, + ctx->src_stride / sizeof(uint32_t), + ctx->dst_stride / sizeof(uint32_t), + ctx->bpp, ctx->bpp, ctx->src.x, ctx->src.y, ctx->dst.x, + ctx->dst.y, ctx->dst.width, ctx->dst.height); #ifdef CONFIG_PIXMAN - int src_stride_words = src_stride / sizeof(uint32_t); - int dst_stride_words = dst_stride / sizeof(uint32_t); - if ((s->use_pixman & BIT(1)) && left_to_right && top_to_bottom) { - fallback = !pixman_blt((uint32_t *)src_bits, (uint32_t *)dst_bits, - src_stride_words, dst_stride_words, bpp, bpp, - src_x, src_y, dst_x, dst_y, - dst_width, dst_height); + int src_stride_words = ctx->src_stride / sizeof(uint32_t); + int dst_stride_words = ctx->dst_stride / sizeof(uint32_t); + if ((s->use_pixman & BIT(1)) && + ctx->left_to_right && ctx->top_to_bottom) { + fallback = !pixman_blt((uint32_t *)ctx->src_bits, + (uint32_t *)ctx->dst_bits, src_stride_words, + dst_stride_words, ctx->bpp, ctx->bpp, + ctx->src.x, ctx->src.y, ctx->dst.x, + ctx->dst.y, ctx->dst.width, ctx->dst.height); } else if (s->use_pixman & BIT(1)) { /* FIXME: We only really need a temporary if src and dst overlap */ - int llb = dst_width * (bpp / 8); + int llb = ctx->dst.width * (ctx->bpp / 8); int tmp_stride_words = DIV_ROUND_UP(llb, sizeof(uint32_t)); uint32_t *tmp = g_malloc(tmp_stride_words * sizeof(uint32_t) * - dst_height); - fallback = !pixman_blt((uint32_t *)src_bits, tmp, - src_stride_words, tmp_stride_words, bpp, bpp, - src_x, src_y, 0, 0, - dst_width, dst_height); + ctx->dst.height); + fallback = !pixman_blt((uint32_t *)ctx->src_bits, tmp, + src_stride_words, tmp_stride_words, ctx->bpp, + ctx->bpp, ctx->src.x, ctx->src.y, 0, 0, + ctx->dst.width, ctx->dst.height); if (!fallback) { - fallback = !pixman_blt(tmp, (uint32_t *)dst_bits, + fallback = !pixman_blt(tmp, (uint32_t *)ctx->dst_bits, tmp_stride_words, dst_stride_words, - bpp, bpp, 0, 0, dst_x, dst_y, - dst_width, dst_height); + ctx->bpp, ctx->bpp, 0, 0, + ctx->dst.x, ctx->dst.y, + ctx->dst.width, ctx->dst.height); } g_free(tmp); } else @@ -168,18 +195,21 @@ void ati_2d_blt(ATIVGAState *s) fallback = true; } if (fallback) { - unsigned int y, i, j, bypp = bpp / 8; - for (y = 0; y < dst_height; y++) { - i = dst_x * bypp; - j = src_x * bypp; - if (top_to_bottom) { - i += (dst_y + y) * dst_stride; - j += (src_y + y) * src_stride; + unsigned int y, i, j, bypp = ctx->bpp / 8; + for (y = 0; y < ctx->dst.height; y++) { + i = ctx->dst.x * bypp; + j = ctx->src.x * bypp; + if (ctx->top_to_bottom) { + i += (ctx->dst.y + y) * ctx->dst_stride; + j += (ctx->src.y + y) * ctx->src_stride; } else { - i += (dst_y + dst_height - 1 - y) * dst_stride; - j += (src_y + dst_height - 1 - y) * src_stride; + i += (ctx->dst.y + ctx->dst.height - 1 - y) + * ctx->dst_stride; + j += (ctx->src.y + ctx->dst.height - 1 - y) + * ctx->src_stride; } - memmove(&dst_bits[i], &src_bits[j], dst_width * bypp); + memmove(&ctx->dst_bits[i], &ctx->src_bits[j], + ctx->dst.width * bypp); } } break; @@ -190,35 +220,40 @@ void ati_2d_blt(ATIVGAState *s) { uint32_t filler = 0; - switch (rop3) { + switch (ctx->rop3) { case ROP3_PATCOPY: - filler = frgd_clr; + filler = ctx->frgd_clr; break; case ROP3_BLACKNESS: - filler = 0xffUL << 24 | rgb_to_pixel32(palette[0], palette[1], - palette[2]); + filler = 0xffUL << 24 | rgb_to_pixel32(ctx->palette[0], + ctx->palette[1], + ctx->palette[2]); break; case ROP3_WHITENESS: - filler = 0xffUL << 24 | rgb_to_pixel32(palette[3], palette[4], - palette[5]); + filler = 0xffUL << 24 | rgb_to_pixel32(ctx->palette[3], + ctx->palette[4], + ctx->palette[5]); break; } DPRINTF("pixman_fill(%p, %ld, %d, %d, %d, %d, %d, %x)\n", - dst_bits, dst_stride / sizeof(uint32_t), bpp, dst_x, dst_y, - dst_width, dst_height, filler); + ctx->dst_bits, ctx->dst_stride / sizeof(uint32_t), ctx->bpp, + ctx->dst.x, ctx->dst.y, ctx->dst.width, ctx->dst.height, + filler); #ifdef CONFIG_PIXMAN if (!(s->use_pixman & BIT(0)) || - !pixman_fill((uint32_t *)dst_bits, dst_stride / sizeof(uint32_t), - bpp, dst_x, dst_y, dst_width, dst_height, filler)) + !pixman_fill((uint32_t *)ctx->dst_bits, + ctx->dst_stride / sizeof(uint32_t), ctx->bpp, + ctx->dst.x, ctx->dst.y, + ctx->dst.width, ctx->dst.height, filler)) #endif { /* fallback when pixman failed or we don't want to call it */ - unsigned int x, y, i, bypp = bpp / 8; - for (y = 0; y < dst_height; y++) { - i = dst_x * bypp + (dst_y + y) * dst_stride; - for (x = 0; x < dst_width; x++, i += bypp) { - stn_he_p(&dst_bits[i], bypp, filler); + unsigned int x, y, i, bypp = ctx->bpp / 8; + for (y = 0; y < ctx->dst.height; y++) { + i = ctx->dst.x * bypp + (ctx->dst.y + y) * ctx->dst_stride; + for (x = 0; x < ctx->dst.width; x++, i += bypp) { + stn_he_p(&ctx->dst_bits[i], bypp, filler); } } } @@ -226,9 +261,9 @@ void ati_2d_blt(ATIVGAState *s) } default: qemu_log_mask(LOG_UNIMP, "Unimplemented ati_2d blt op %x\n", - rop3 >> 16); + ctx->rop3 >> 16); return; } - ati_set_dirty(s, dst_bits, dst_y); + ati_set_dirty(&s->vga, ctx); } From b6740448a22922a828be60c67c93877b2f5f401d Mon Sep 17 00:00:00 2001 From: Chad Jablonski Date: Mon, 9 Mar 2026 02:47:47 +0100 Subject: [PATCH 39/49] ati-vga: Extract setup_2d_blt_ctx from ati_2d_blt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit setup_2d_blt_ctx is responsible for knowing how to retrieve the state needed by ati_2d_blt from the registers and assigning it to the ATI2DCtx. This will be useful in a future patch when HOST_DATA needs to make small modifications to the ctx. Signed-off-by: Chad Jablonski Reviewed-by: BALATON Zoltan [balaton: Fix build without pixman] Signed-off-by: BALATON Zoltan Message-ID: <6e2dccba65d9b369e0db633c0149a77351827222.1773020351.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/ati_2d.c | 69 +++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c index 1ae2fbc5f8..ef4d2e21b5 100644 --- a/hw/display/ati_2d.c +++ b/hw/display/ati_2d.c @@ -25,7 +25,7 @@ * possible. */ -static int ati_bpp_from_datatype(ATIVGAState *s) +static int ati_bpp_from_datatype(const ATIVGAState *s) { switch (s->regs.dp_datatype & 0xf) { case 2: @@ -80,73 +80,76 @@ static void ati_set_dirty(VGACommonState *vga, const ATI2DCtx *ctx) } } -void ati_2d_blt(ATIVGAState *s) +static void setup_2d_blt_ctx(const ATIVGAState *s, ATI2DCtx *ctx) { - /* FIXME it is probably more complex than this and may need to be */ - /* rewritten but for now as a start just to get some output: */ - ATI2DCtx ctx_; - ATI2DCtx *ctx = &ctx_; + ctx->bpp = ati_bpp_from_datatype(s); ctx->rop3 = s->regs.dp_mix & GMC_ROP3_MASK; ctx->left_to_right = s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT; ctx->top_to_bottom = s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM; 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->dst.width = s->regs.dst_width; ctx->dst.height = s->regs.dst_height; ctx->dst.x = (ctx->left_to_right ? s->regs.dst_x : s->regs.dst_x + 1 - ctx->dst.width); ctx->dst.y = (ctx->top_to_bottom ? s->regs.dst_y : s->regs.dst_y + 1 - ctx->dst.height); - ctx->bpp = ati_bpp_from_datatype(s); + ctx->dst_stride = s->regs.dst_pitch; + ctx->dst_bits = s->vga.vram_ptr + s->regs.dst_offset; + if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) { + ctx->dst_bits += s->regs.crtc_offset & 0x07ffffff; + ctx->dst_stride *= ctx->bpp; + } + + ctx->src.x = (ctx->left_to_right ? + s->regs.src_x : s->regs.src_x + 1 - ctx->dst.width); + ctx->src.y = (ctx->top_to_bottom ? + s->regs.src_y : s->regs.src_y + 1 - ctx->dst.height); + ctx->src_stride = s->regs.src_pitch; + ctx->src_bits = s->vga.vram_ptr + s->regs.src_offset; + if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) { + ctx->src_bits += s->regs.crtc_offset & 0x07ffffff; + ctx->src_stride *= ctx->bpp; + } + DPRINTF("%d %d %d, %d %d %d, (%d,%d) -> (%d,%d) %dx%d %c %c\n", + s->regs.src_offset, s->regs.dst_offset, s->regs.default_offset, + ctx->src_stride, ctx->dst_stride, s->regs.default_pitch, + ctx->src.x, ctx->src.y, ctx->dst.x, ctx->dst.y, + ctx->dst.width, ctx->dst.height, + (ctx->left_to_right ? '>' : '<'), + (ctx->top_to_bottom ? 'v' : '^')); +} + +void ati_2d_blt(ATIVGAState *s) +{ + ATI2DCtx ctx_; + ATI2DCtx *ctx = &ctx_; + setup_2d_blt_ctx(s, ctx); if (!ctx->bpp) { qemu_log_mask(LOG_GUEST_ERROR, "Invalid bpp\n"); return; } - ctx->dst_stride = s->regs.dst_pitch; if (!ctx->dst_stride) { qemu_log_mask(LOG_GUEST_ERROR, "Zero dest pitch\n"); return; } - ctx->dst_bits = s->vga.vram_ptr + ctx->dst_offset; - - if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) { - ctx->dst_bits += s->regs.crtc_offset & 0x07ffffff; - ctx->dst_stride *= ctx->bpp; - } - ctx->vram_end = s->vga.vram_ptr + s->vga.vram_size; if (ctx->dst.x > 0x3fff || ctx->dst.y > 0x3fff || ctx->dst_bits >= ctx->vram_end || ctx->dst_bits + ctx->dst.x + (ctx->dst.y + ctx->dst.height) * ctx->dst_stride >= ctx->vram_end) { qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n"); return; } - DPRINTF("%d %d %d, %d %d %d, (%d,%d) -> (%d,%d) %dx%d %c %c\n", - s->regs.src_offset, ctx->dst_offset, s->regs.default_offset, - ctx->src_stride, ctx->dst_stride, s->regs.default_pitch, - ctx->src.x, ctx->src.y, ctx->dst.x, ctx->dst.y, - ctx->dst.width, ctx->dst.height, - (ctx->left_to_right ? '>' : '<'), - (ctx->top_to_bottom ? 'v' : '^')); switch (ctx->rop3) { case ROP3_SRCCOPY: { bool fallback = false; - ctx->src.x = (ctx->left_to_right ? - s->regs.src_x : s->regs.src_x + 1 - ctx->dst.width); - ctx->src.y = (ctx->top_to_bottom ? - s->regs.src_y : s->regs.src_y + 1 - ctx->dst.height); - ctx->src_stride = s->regs.src_pitch; if (!ctx->src_stride) { qemu_log_mask(LOG_GUEST_ERROR, "Zero source pitch\n"); return; } - ctx->src_bits = s->vga.vram_ptr + s->regs.src_offset; - - if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) { - ctx->src_bits += s->regs.crtc_offset & 0x07ffffff; - ctx->src_stride *= ctx->bpp; - } if (ctx->src.x > 0x3fff || ctx->src.y > 0x3fff || ctx->src_bits >= ctx->vram_end || ctx->src_bits + ctx->src.x + (ctx->src.y + ctx->dst.height) * From 4066a2ff2715be7a012200a31b6cc1008e4f5b9a Mon Sep 17 00:00:00 2001 From: Chad Jablonski Date: Mon, 9 Mar 2026 02:47:48 +0100 Subject: [PATCH 40/49] ati-vga: Split ati_2d_do_blt from ati_2d_blt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ati_2d_blt remains the public interface to the blitter but the bulk of the implementation is moved down into ati_2d_do_blt which is passed an ATI2DCtx. ati_2d_do_blt returns a bool that is true when the blit succeeded, which means that a screen region will need to be set dirty. Otherwise false is returned. Signed-off-by: Chad Jablonski Reviewed-by: BALATON Zoltan [balaton: Fix build without pixman] Signed-off-by: BALATON Zoltan Message-ID: <367949c50ca140a2d18ae66234dafbbc586b553c.1773020351.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/ati_2d.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c index ef4d2e21b5..aa2e540a51 100644 --- a/hw/display/ati_2d.c +++ b/hw/display/ati_2d.c @@ -123,24 +123,21 @@ static void setup_2d_blt_ctx(const ATIVGAState *s, ATI2DCtx *ctx) (ctx->top_to_bottom ? 'v' : '^')); } -void ati_2d_blt(ATIVGAState *s) +static bool ati_2d_do_blt(ATIVGAState *s, ATI2DCtx *ctx) { - ATI2DCtx ctx_; - ATI2DCtx *ctx = &ctx_; - setup_2d_blt_ctx(s, ctx); if (!ctx->bpp) { qemu_log_mask(LOG_GUEST_ERROR, "Invalid bpp\n"); - return; + return false; } if (!ctx->dst_stride) { qemu_log_mask(LOG_GUEST_ERROR, "Zero dest pitch\n"); - return; + return false; } if (ctx->dst.x > 0x3fff || ctx->dst.y > 0x3fff || ctx->dst_bits >= ctx->vram_end || ctx->dst_bits + ctx->dst.x + (ctx->dst.y + ctx->dst.height) * ctx->dst_stride >= ctx->vram_end) { qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n"); - return; + return false; } switch (ctx->rop3) { case ROP3_SRCCOPY: @@ -148,14 +145,14 @@ void ati_2d_blt(ATIVGAState *s) bool fallback = false; if (!ctx->src_stride) { qemu_log_mask(LOG_GUEST_ERROR, "Zero source pitch\n"); - return; + return false; } if (ctx->src.x > 0x3fff || ctx->src.y > 0x3fff || ctx->src_bits >= ctx->vram_end || ctx->src_bits + ctx->src.x + (ctx->src.y + ctx->dst.height) * ctx->src_stride >= ctx->vram_end) { qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n"); - return; + return false; } DPRINTF("pixman_blt(%p, %p, %ld, %ld, %d, %d, %d, %d, %d, %d, %d, %d)\n", @@ -265,8 +262,17 @@ void ati_2d_blt(ATIVGAState *s) default: qemu_log_mask(LOG_UNIMP, "Unimplemented ati_2d blt op %x\n", ctx->rop3 >> 16); - return; + return false; } - ati_set_dirty(&s->vga, ctx); + return true; +} + +void ati_2d_blt(ATIVGAState *s) +{ + ATI2DCtx ctx; + setup_2d_blt_ctx(s, &ctx); + if (ati_2d_do_blt(s, &ctx)) { + ati_set_dirty(&s->vga, &ctx); + } } From ff3da02eaffa31e8e708d101331a6c4dcec0f171 Mon Sep 17 00:00:00 2001 From: Chad Jablonski Date: Mon, 9 Mar 2026 02:47:49 +0100 Subject: [PATCH 41/49] ati-vga: Remove ATIVGAState param from ati_2d_do_blt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This completes the decoupling from the ATIVGAState struct. Signed-off-by: Chad Jablonski Reviewed-by: BALATON Zoltan [balaton: Fix build without pixman] Signed-off-by: BALATON Zoltan Message-ID: <6e3545dea9f4522e07e91f9db885a7d74dae781a.1773020351.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/ati_2d.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c index aa2e540a51..be940d8ab6 100644 --- a/hw/display/ati_2d.c +++ b/hw/display/ati_2d.c @@ -123,7 +123,7 @@ static void setup_2d_blt_ctx(const ATIVGAState *s, ATI2DCtx *ctx) (ctx->top_to_bottom ? 'v' : '^')); } -static bool ati_2d_do_blt(ATIVGAState *s, ATI2DCtx *ctx) +static bool ati_2d_do_blt(ATI2DCtx *ctx, uint8_t use_pixman) { if (!ctx->bpp) { qemu_log_mask(LOG_GUEST_ERROR, "Invalid bpp\n"); @@ -164,14 +164,14 @@ static bool ati_2d_do_blt(ATIVGAState *s, ATI2DCtx *ctx) #ifdef CONFIG_PIXMAN int src_stride_words = ctx->src_stride / sizeof(uint32_t); int dst_stride_words = ctx->dst_stride / sizeof(uint32_t); - if ((s->use_pixman & BIT(1)) && + if ((use_pixman & BIT(1)) && ctx->left_to_right && ctx->top_to_bottom) { fallback = !pixman_blt((uint32_t *)ctx->src_bits, (uint32_t *)ctx->dst_bits, src_stride_words, dst_stride_words, ctx->bpp, ctx->bpp, ctx->src.x, ctx->src.y, ctx->dst.x, ctx->dst.y, ctx->dst.width, ctx->dst.height); - } else if (s->use_pixman & BIT(1)) { + } else if (use_pixman & BIT(1)) { /* FIXME: We only really need a temporary if src and dst overlap */ int llb = ctx->dst.width * (ctx->bpp / 8); int tmp_stride_words = DIV_ROUND_UP(llb, sizeof(uint32_t)); @@ -241,7 +241,7 @@ static bool ati_2d_do_blt(ATIVGAState *s, ATI2DCtx *ctx) ctx->dst.x, ctx->dst.y, ctx->dst.width, ctx->dst.height, filler); #ifdef CONFIG_PIXMAN - if (!(s->use_pixman & BIT(0)) || + if (!(use_pixman & BIT(0)) || !pixman_fill((uint32_t *)ctx->dst_bits, ctx->dst_stride / sizeof(uint32_t), ctx->bpp, ctx->dst.x, ctx->dst.y, @@ -272,7 +272,7 @@ void ati_2d_blt(ATIVGAState *s) { ATI2DCtx ctx; setup_2d_blt_ctx(s, &ctx); - if (ati_2d_do_blt(s, &ctx)) { + if (ati_2d_do_blt(&ctx, s->use_pixman)) { ati_set_dirty(&s->vga, &ctx); } } From 527b07d2c59f2cda5186476b9d8b79371102689b Mon Sep 17 00:00:00 2001 From: Chad Jablonski Date: Mon, 9 Mar 2026 02:47:50 +0100 Subject: [PATCH 42/49] ati-vga: Implement scissor rectangle clipping for 2D operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use scissor registers to clip blit operations. This is required for text rendering in X using the r128 driver. Without it overly-wide glyphs are drawn and create all sorts of chaos. The visible destination rectangle (vis_dst) is the intersection of the scissor rectangle and the destination rectangle (dst). The src also needs to be offset if clipped on the top and/or left sides to ensure that src data is read correctly and appears clipped when drawn rather than shifted. Signed-off-by: Chad Jablonski Reviewed-by: BALATON Zoltan [balaton: Fix build without pixman] Signed-off-by: BALATON Zoltan Message-ID: <4752dfbe240102c2ffa1f2cdab4c9442d4f0dcfb.1773020351.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/ati_2d.c | 87 ++++++++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 29 deletions(-) diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c index be940d8ab6..1cfd350769 100644 --- a/hw/display/ati_2d.c +++ b/hw/display/ati_2d.c @@ -52,6 +52,7 @@ typedef struct { uint32_t frgd_clr; const uint8_t *palette; const uint8_t *vram_end; + QemuRect scissor; QemuRect dst; int dst_stride; @@ -91,6 +92,11 @@ static void setup_2d_blt_ctx(const ATIVGAState *s, ATI2DCtx *ctx) 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; + ctx->scissor.x = s->regs.sc_left; + ctx->scissor.y = s->regs.sc_top; + ctx->dst.width = s->regs.dst_width; ctx->dst.height = s->regs.dst_height; ctx->dst.x = (ctx->left_to_right ? @@ -125,6 +131,8 @@ static void setup_2d_blt_ctx(const ATIVGAState *s, ATI2DCtx *ctx) static bool ati_2d_do_blt(ATI2DCtx *ctx, uint8_t use_pixman) { + QemuRect vis_src, vis_dst; + if (!ctx->bpp) { qemu_log_mask(LOG_GUEST_ERROR, "Invalid bpp\n"); return false; @@ -139,6 +147,29 @@ static bool ati_2d_do_blt(ATI2DCtx *ctx, uint8_t use_pixman) qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n"); return false; } + qemu_rect_intersect(&ctx->dst, &ctx->scissor, &vis_dst); + if (!vis_dst.height || !vis_dst.width) { + /* Nothing is visible, completely clipped */ + return false; + } + /* + * The src must be offset if clipping is applied to the dst. + * This is so that when the source is blit to a dst clipped + * on the top or left the src image is not shifted into the + * clipped region but actually clipped. + */ + vis_src.x = ctx->src.x + (vis_dst.x - ctx->dst.x); + vis_src.y = ctx->src.y + (vis_dst.y - ctx->dst.y); + vis_src.width = vis_dst.width; + vis_src.height = vis_dst.height; + + DPRINTF("dst: (%d,%d) %dx%d -> vis_dst: (%d,%d) %dx%d\n", + ctx->dst.x, ctx->dst.y, ctx->dst.width, ctx->dst.height, + vis_dst.x, vis_dst.y, vis_dst.width, vis_dst.height); + DPRINTF("src: (%d,%d) %dx%d -> vis_src: (%d,%d) %dx%d\n", + ctx->src.x, ctx->src.y, ctx->dst.width, ctx->dst.height, + vis_src.x, vis_src.y, vis_src.width, vis_src.height); + switch (ctx->rop3) { case ROP3_SRCCOPY: { @@ -147,10 +178,9 @@ static bool ati_2d_do_blt(ATI2DCtx *ctx, uint8_t use_pixman) qemu_log_mask(LOG_GUEST_ERROR, "Zero source pitch\n"); return false; } - if (ctx->src.x > 0x3fff || ctx->src.y > 0x3fff || - ctx->src_bits >= ctx->vram_end || - ctx->src_bits + ctx->src.x + (ctx->src.y + ctx->dst.height) * - ctx->src_stride >= ctx->vram_end) { + if (vis_src.x > 0x3fff || vis_src.y > 0x3fff || + ctx->src_bits >= ctx->vram_end || ctx->src_bits + vis_src.x + + (vis_src.y + vis_dst.height) * ctx->src_stride >= ctx->vram_end) { qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n"); return false; } @@ -159,8 +189,8 @@ static bool ati_2d_do_blt(ATI2DCtx *ctx, uint8_t use_pixman) ctx->src_bits, ctx->dst_bits, ctx->src_stride / sizeof(uint32_t), ctx->dst_stride / sizeof(uint32_t), - ctx->bpp, ctx->bpp, ctx->src.x, ctx->src.y, ctx->dst.x, - ctx->dst.y, ctx->dst.width, ctx->dst.height); + ctx->bpp, ctx->bpp, vis_src.x, vis_src.y, vis_dst.x, vis_dst.y, + vis_dst.width, vis_dst.height); #ifdef CONFIG_PIXMAN int src_stride_words = ctx->src_stride / sizeof(uint32_t); int dst_stride_words = ctx->dst_stride / sizeof(uint32_t); @@ -169,24 +199,24 @@ static bool ati_2d_do_blt(ATI2DCtx *ctx, uint8_t use_pixman) fallback = !pixman_blt((uint32_t *)ctx->src_bits, (uint32_t *)ctx->dst_bits, src_stride_words, dst_stride_words, ctx->bpp, ctx->bpp, - ctx->src.x, ctx->src.y, ctx->dst.x, - ctx->dst.y, ctx->dst.width, ctx->dst.height); + vis_src.x, vis_src.y, vis_dst.x, vis_dst.y, + vis_dst.width, vis_dst.height); } else if (use_pixman & BIT(1)) { /* FIXME: We only really need a temporary if src and dst overlap */ - int llb = ctx->dst.width * (ctx->bpp / 8); + int llb = vis_dst.width * (ctx->bpp / 8); int tmp_stride_words = DIV_ROUND_UP(llb, sizeof(uint32_t)); uint32_t *tmp = g_malloc(tmp_stride_words * sizeof(uint32_t) * - ctx->dst.height); + vis_dst.height); fallback = !pixman_blt((uint32_t *)ctx->src_bits, tmp, src_stride_words, tmp_stride_words, ctx->bpp, - ctx->bpp, ctx->src.x, ctx->src.y, 0, 0, - ctx->dst.width, ctx->dst.height); + ctx->bpp, vis_src.x, vis_src.y, 0, 0, + vis_dst.width, vis_dst.height); if (!fallback) { fallback = !pixman_blt(tmp, (uint32_t *)ctx->dst_bits, tmp_stride_words, dst_stride_words, ctx->bpp, ctx->bpp, 0, 0, - ctx->dst.x, ctx->dst.y, - ctx->dst.width, ctx->dst.height); + vis_dst.x, vis_dst.y, + vis_dst.width, vis_dst.height); } g_free(tmp); } else @@ -196,20 +226,20 @@ static bool ati_2d_do_blt(ATI2DCtx *ctx, uint8_t use_pixman) } if (fallback) { unsigned int y, i, j, bypp = ctx->bpp / 8; - for (y = 0; y < ctx->dst.height; y++) { - i = ctx->dst.x * bypp; - j = ctx->src.x * bypp; + for (y = 0; y < vis_dst.height; y++) { + i = vis_dst.x * bypp; + j = vis_src.x * bypp; if (ctx->top_to_bottom) { - i += (ctx->dst.y + y) * ctx->dst_stride; - j += (ctx->src.y + y) * ctx->src_stride; + i += (vis_dst.y + y) * ctx->dst_stride; + j += (vis_src.y + y) * ctx->src_stride; } else { - i += (ctx->dst.y + ctx->dst.height - 1 - y) + i += (vis_dst.y + vis_dst.height - 1 - y) * ctx->dst_stride; - j += (ctx->src.y + ctx->dst.height - 1 - y) + j += (vis_src.y + vis_dst.height - 1 - y) * ctx->src_stride; } memmove(&ctx->dst_bits[i], &ctx->src_bits[j], - ctx->dst.width * bypp); + vis_dst.width * bypp); } } break; @@ -238,21 +268,20 @@ static bool ati_2d_do_blt(ATI2DCtx *ctx, uint8_t use_pixman) DPRINTF("pixman_fill(%p, %ld, %d, %d, %d, %d, %d, %x)\n", ctx->dst_bits, ctx->dst_stride / sizeof(uint32_t), ctx->bpp, - ctx->dst.x, ctx->dst.y, ctx->dst.width, ctx->dst.height, - filler); + vis_dst.x, vis_dst.y, vis_dst.width, vis_dst.height, filler); #ifdef CONFIG_PIXMAN if (!(use_pixman & BIT(0)) || !pixman_fill((uint32_t *)ctx->dst_bits, ctx->dst_stride / sizeof(uint32_t), ctx->bpp, - ctx->dst.x, ctx->dst.y, - ctx->dst.width, ctx->dst.height, filler)) + vis_dst.x, vis_dst.y, vis_dst.width, vis_dst.height, + filler)) #endif { /* fallback when pixman failed or we don't want to call it */ unsigned int x, y, i, bypp = ctx->bpp / 8; - for (y = 0; y < ctx->dst.height; y++) { - i = ctx->dst.x * bypp + (ctx->dst.y + y) * ctx->dst_stride; - for (x = 0; x < ctx->dst.width; x++, i += bypp) { + for (y = 0; y < vis_dst.height; y++) { + i = vis_dst.x * bypp + (vis_dst.y + y) * ctx->dst_stride; + for (x = 0; x < vis_dst.width; x++, i += bypp) { stn_he_p(&ctx->dst_bits[i], bypp, filler); } } From 69051f729317337d58c3f847efbb8593a5a0ba26 Mon Sep 17 00:00:00 2001 From: Chad Jablonski Date: Mon, 9 Mar 2026 02:47:51 +0100 Subject: [PATCH 43/49] ati-vga: Implement HOST_DATA register writes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Writing to any of the HOST_DATA0-7 registers pushes the written data into a 128-bit accumulator. When the accumulator is full a flush is triggered to copy it to the framebuffer. A final write to HOST_DATA_LAST will also initiate a flush. The flush itself is left for the next patch. Unaligned HOST_DATA* writes result in, from what I can tell, undefined behavior on real hardware. A well-behaved driver shouldn't be doing this anyway. For that reason they are not handled here at all. Signed-off-by: Chad Jablonski Reviewed-by: BALATON Zoltan Signed-off-by: BALATON Zoltan Message-ID: <235adb69787c818afd023e7f32286166cc70744c.1773020351.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/ati.c | 26 ++++++++++++++++++++++++++ hw/display/ati_dbg.c | 9 +++++++++ hw/display/ati_int.h | 9 +++++++++ hw/display/ati_regs.h | 9 +++++++++ 4 files changed, 53 insertions(+) diff --git a/hw/display/ati.c b/hw/display/ati.c index 9a7c5703b0..16dbb743e1 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -1023,6 +1023,27 @@ static void ati_mm_write(void *opaque, hwaddr addr, case SRC_SC_BOTTOM: s->regs.src_sc_bottom = data & 0x3fff; break; + case HOST_DATA0: + case HOST_DATA1: + case HOST_DATA2: + case HOST_DATA3: + case HOST_DATA4: + case HOST_DATA5: + case HOST_DATA6: + case HOST_DATA7: + case HOST_DATA_LAST: + if (!s->host_data.active) { + break; + } + s->host_data.acc[s->host_data.next++] = data; + if (addr == HOST_DATA_LAST) { + qemu_log_mask(LOG_UNIMP, "HOST_DATA finish not yet implemented\n"); + s->host_data.next = 0; + } else if (s->host_data.next >= 4) { + qemu_log_mask(LOG_UNIMP, "HOST_DATA flush not yet implemented\n"); + s->host_data.next = 0; + } + break; default: break; } @@ -1128,6 +1149,11 @@ static void ati_vga_reset(DeviceState *dev) /* reset vga */ vga_common_reset(&s->vga); s->mode = VGA_MODE; + + s->host_data.active = false; + s->host_data.next = 0; + s->host_data.row = 0; + s->host_data.col = 0; } static void ati_vga_exit(PCIDevice *dev) diff --git a/hw/display/ati_dbg.c b/hw/display/ati_dbg.c index 3ffa7f35df..5c799d540a 100644 --- a/hw/display/ati_dbg.c +++ b/hw/display/ati_dbg.c @@ -252,6 +252,15 @@ static struct ati_regdesc ati_reg_names[] = { {"MC_SRC1_CNTL", 0x19D8}, {"TEX_CNTL", 0x1800}, {"RAGE128_MPP_TB_CONFIG", 0x01c0}, + {"HOST_DATA0", 0x17c0}, + {"HOST_DATA1", 0x17c4}, + {"HOST_DATA2", 0x17c8}, + {"HOST_DATA3", 0x17cc}, + {"HOST_DATA4", 0x17d0}, + {"HOST_DATA5", 0x17d4}, + {"HOST_DATA6", 0x17d8}, + {"HOST_DATA7", 0x17dc}, + {"HOST_DATA_LAST", 0x17e0}, {NULL, -1} }; diff --git a/hw/display/ati_int.h b/hw/display/ati_int.h index 874c63eb54..063efc7bba 100644 --- a/hw/display/ati_int.h +++ b/hw/display/ati_int.h @@ -96,6 +96,14 @@ typedef struct ATIVGARegs { uint32_t default_tile; } ATIVGARegs; +typedef struct ATIHostDataState { + bool active; + uint32_t row; + uint32_t col; + uint32_t next; + uint32_t acc[4]; +} ATIHostDataState; + struct ATIVGAState { PCIDevice dev; VGACommonState vga; @@ -114,6 +122,7 @@ struct ATIVGAState { MemoryRegion io; MemoryRegion mm; ATIVGARegs regs; + ATIHostDataState host_data; }; const char *ati_reg_name(int num); diff --git a/hw/display/ati_regs.h b/hw/display/ati_regs.h index 3999edb9b7..48f15e9b1d 100644 --- a/hw/display/ati_regs.h +++ b/hw/display/ati_regs.h @@ -252,6 +252,15 @@ #define DP_T12_CNTL 0x178c #define DST_BRES_T1_LNTH 0x1790 #define DST_BRES_T2_LNTH 0x1794 +#define HOST_DATA0 0x17c0 +#define HOST_DATA1 0x17c4 +#define HOST_DATA2 0x17c8 +#define HOST_DATA3 0x17cc +#define HOST_DATA4 0x17d0 +#define HOST_DATA5 0x17d4 +#define HOST_DATA6 0x17d8 +#define HOST_DATA7 0x17dc +#define HOST_DATA_LAST 0x17e0 #define SCALE_SRC_HEIGHT_WIDTH 0x1994 #define SCALE_OFFSET_0 0x1998 #define SCALE_PITCH 0x199c From 2735701ec957c7893b7bb82590dcf80cdd817bca Mon Sep 17 00:00:00 2001 From: Chad Jablonski Date: Mon, 9 Mar 2026 02:47:52 +0100 Subject: [PATCH 44/49] ati-vga: Implement HOST_DATA flush to VRAM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement flushing the 128-bit HOST_DATA accumulator to VRAM to enable text rendering in X. Supports all datatypes (monochrome frgd/bkgd, monochrome frgd, and color), however monochrome frgd support is partial and does not properly handle transparency/leave-alone. The flush is broken up into two steps. First, if necessary, expansion of the monochrome bits to the destination color depth. Then the expanded pixels are sent to the ati_2d_do_blt one scanline at a time. ati_2d_do_blt then clips and performs the blit. Signed-off-by: Chad Jablonski Reviewed-by: BALATON Zoltan [balaton: Fix build without pixman] Signed-off-by: BALATON Zoltan Message-ID: <8d0d4b52a16f5564e37bbc88496d7ab8d41477ac.1773020351.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/ati.c | 6 +- hw/display/ati_2d.c | 132 +++++++++++++++++++++++++++++++++++++++++- hw/display/ati_int.h | 3 + hw/display/ati_regs.h | 4 ++ 4 files changed, 139 insertions(+), 6 deletions(-) diff --git a/hw/display/ati.c b/hw/display/ati.c index 16dbb743e1..bb9e0bba28 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -1037,11 +1037,9 @@ static void ati_mm_write(void *opaque, hwaddr addr, } s->host_data.acc[s->host_data.next++] = data; if (addr == HOST_DATA_LAST) { - qemu_log_mask(LOG_UNIMP, "HOST_DATA finish not yet implemented\n"); - s->host_data.next = 0; + ati_host_data_finish(s); } else if (s->host_data.next >= 4) { - qemu_log_mask(LOG_UNIMP, "HOST_DATA flush not yet implemented\n"); - s->host_data.next = 0; + ati_host_data_flush(s); } break; default: diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c index 1cfd350769..37fe6c17ee 100644 --- a/hw/display/ati_2d.c +++ b/hw/display/ati_2d.c @@ -47,6 +47,7 @@ static int ati_bpp_from_datatype(const ATIVGAState *s) typedef struct { int bpp; uint32_t rop3; + bool host_data_active; bool left_to_right; bool top_to_bottom; uint32_t frgd_clr; @@ -85,6 +86,7 @@ static void setup_2d_blt_ctx(const ATIVGAState *s, ATI2DCtx *ctx) { ctx->bpp = ati_bpp_from_datatype(s); ctx->rop3 = s->regs.dp_mix & GMC_ROP3_MASK; + ctx->host_data_active = s->host_data.active; ctx->left_to_right = s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT; ctx->top_to_bottom = s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM; ctx->frgd_clr = s->regs.dp_brush_frgd_clr; @@ -178,9 +180,10 @@ static bool ati_2d_do_blt(ATI2DCtx *ctx, uint8_t use_pixman) qemu_log_mask(LOG_GUEST_ERROR, "Zero source pitch\n"); return false; } - if (vis_src.x > 0x3fff || vis_src.y > 0x3fff || + if (!ctx->host_data_active && + (vis_src.x > 0x3fff || vis_src.y > 0x3fff || ctx->src_bits >= ctx->vram_end || ctx->src_bits + vis_src.x + - (vis_src.y + vis_dst.height) * ctx->src_stride >= ctx->vram_end) { + (vis_src.y + vis_dst.height) * ctx->src_stride >= ctx->vram_end)) { qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n"); return false; } @@ -300,8 +303,133 @@ static bool ati_2d_do_blt(ATI2DCtx *ctx, uint8_t use_pixman) void ati_2d_blt(ATIVGAState *s) { ATI2DCtx ctx; + uint32_t src_source = s->regs.dp_mix & DP_SRC_SOURCE; + + /* Finish any active HOST_DATA blits before starting a new blit */ + ati_host_data_finish(s); + + if (src_source == DP_SRC_HOST || src_source == DP_SRC_HOST_BYTEALIGN) { + /* Begin a HOST_DATA blit */ + s->host_data.active = true; + s->host_data.next = 0; + s->host_data.col = 0; + s->host_data.row = 0; + return; + } setup_2d_blt_ctx(s, &ctx); if (ati_2d_do_blt(&ctx, s->use_pixman)) { ati_set_dirty(&s->vga, &ctx); } } + +bool ati_host_data_flush(ATIVGAState *s) +{ + ATI2DCtx ctx, chunk; + uint32_t fg = s->regs.dp_src_frgd_clr; + uint32_t bg = s->regs.dp_src_bkgd_clr; + unsigned bypp, pix_count, row, col, idx; + uint8_t pix_buf[ATI_HOST_DATA_ACC_BITS * sizeof(uint32_t)]; + uint32_t byte_pix_order = s->regs.dp_datatype & DP_BYTE_PIX_ORDER; + uint32_t src_source = s->regs.dp_mix & DP_SRC_SOURCE; + uint32_t src_datatype = s->regs.dp_datatype & DP_SRC_DATATYPE; + + if (!s->host_data.active) { + return false; + } + if (src_source != DP_SRC_HOST) { + qemu_log_mask(LOG_GUEST_ERROR, + "host_data_blt: unsupported src_source %x\n", src_source); + return false; + } + if (src_datatype != SRC_MONO_FRGD_BKGD && src_datatype != SRC_MONO_FRGD && + src_datatype != SRC_COLOR) { + qemu_log_mask(LOG_GUEST_ERROR, + "host_data_blt: undefined src_datatype %x\n", + src_datatype); + return false; + } + + setup_2d_blt_ctx(s, &ctx); + + if (!ctx.left_to_right || !ctx.top_to_bottom) { + qemu_log_mask(LOG_UNIMP, + "host_data_blt: unsupported blit direction %c%c\n", + ctx.left_to_right ? '>' : '<', + ctx.top_to_bottom ? 'v' : '^'); + return false; + } + + bypp = ctx.bpp / 8; + + if (src_datatype == SRC_COLOR) { + pix_count = ATI_HOST_DATA_ACC_BITS / ctx.bpp; + memcpy(pix_buf, &s->host_data.acc[0], sizeof(s->host_data.acc)); + } else { + pix_count = ATI_HOST_DATA_ACC_BITS; + /* Expand monochrome bits to color pixels */ + idx = 0; + for (int word = 0; word < 4; word++) { + for (int byte = 0; byte < 4; byte++) { + uint8_t byte_val = s->host_data.acc[word] >> (byte * 8); + for (int i = 0; i < 8; i++) { + bool is_fg = byte_val & BIT(byte_pix_order ? i : 7 - i); + uint32_t color = is_fg ? fg : bg; + stn_he_p(&pix_buf[idx], bypp, color); + idx += bypp; + } + } + } + } + + /* Copy and then modify blit ctx for use in a chunked blit */ + chunk = ctx; + chunk.src_bits = pix_buf; + chunk.src.y = 0; + chunk.src_stride = ATI_HOST_DATA_ACC_BITS * bypp; + + /* Blit one scanline chunk at a time */ + row = s->host_data.row; + col = s->host_data.col; + idx = 0; + DPRINTF("blt %dpx @ row: %d, col: %d\n", pix_count, row, col); + while (idx < pix_count && row < ctx.dst.height) { + unsigned pix_in_scanline = MIN(pix_count - idx, + ctx.dst.width - col); + chunk.src.x = idx; + /* Build a rect for this scanline chunk */ + chunk.dst.x = ctx.dst.x + col; + chunk.dst.y = ctx.dst.y + row; + chunk.dst.width = pix_in_scanline; + chunk.dst.height = 1; + 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); + } + idx += pix_in_scanline; + col += pix_in_scanline; + if (col >= ctx.dst.width) { + col = 0; + row += 1; + } + } + + /* 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) { + s->host_data.active = false; + } + + return s->host_data.active; +} + +void ati_host_data_finish(ATIVGAState *s) +{ + if (ati_host_data_flush(s)) { + qemu_log_mask(LOG_GUEST_ERROR, + "HOST_DATA blit ended before all data was written\n"); + } + s->host_data.active = false; +} diff --git a/hw/display/ati_int.h b/hw/display/ati_int.h index 063efc7bba..21b74511e0 100644 --- a/hw/display/ati_int.h +++ b/hw/display/ati_int.h @@ -33,6 +33,7 @@ #define ATI_RAGE128_LINEAR_APER_SIZE (64 * MiB) #define ATI_R100_LINEAR_APER_SIZE (128 * MiB) +#define ATI_HOST_DATA_ACC_BITS 128 #define TYPE_ATI_VGA "ati-vga" OBJECT_DECLARE_SIMPLE_TYPE(ATIVGAState, ATI_VGA) @@ -128,5 +129,7 @@ struct ATIVGAState { const char *ati_reg_name(int num); void ati_2d_blt(ATIVGAState *s); +bool ati_host_data_flush(ATIVGAState *s); +void ati_host_data_finish(ATIVGAState *s); #endif /* ATI_INT_H */ diff --git a/hw/display/ati_regs.h b/hw/display/ati_regs.h index 48f15e9b1d..b813fa119e 100644 --- a/hw/display/ati_regs.h +++ b/hw/display/ati_regs.h @@ -397,7 +397,11 @@ #define DST_32BPP 0x00000006 #define DP_DST_DATATYPE 0x0000000f #define DP_BRUSH_DATATYPE 0x00000f00 +#define SRC_MONO_FRGD_BKGD 0x00000000 +#define SRC_MONO_FRGD 0x00010000 +#define SRC_COLOR 0x00030000 #define DP_SRC_DATATYPE 0x00030000 +#define DP_BYTE_PIX_ORDER 0x40000000 #define BRUSH_SOLIDCOLOR 0x00000d00 From 1c0f729127f39dbcbdbc812b3c7465104c2a6fcf Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sun, 8 Mar 2026 23:49:16 +0100 Subject: [PATCH 45/49] ati-vga: Simplify hw cursor drawing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid calculating index at every step when we can just count the position. Signed-off-by: BALATON Zoltan Reviewed-by: Chad Jablonski Message-ID: <90b49357b6bf80bcf8962331575ffd16a96b706a.1773009887.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/ati.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/display/ati.c b/hw/display/ati.c index bb9e0bba28..2c649e940b 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -198,7 +198,7 @@ static void ati_cursor_draw_line(VGACommonState *vga, uint8_t *d, int scr_y) ATIVGAState *s = container_of(vga, ATIVGAState, vga); uint32_t srcoff; uint32_t *dp = (uint32_t *)d; - int i, j, h; + int i, j, h, idx = 0; if (!(s->regs.crtc_gen_cntl & CRTC2_CUR_EN) || scr_y < vga->hw_cursor_y || scr_y >= vga->hw_cursor_y + 64 || @@ -213,10 +213,10 @@ static void ati_cursor_draw_line(VGACommonState *vga, uint8_t *d, int scr_y) uint32_t color; uint8_t abits = vga_read_byte(vga, srcoff + i); uint8_t xbits = vga_read_byte(vga, srcoff + i + 8); - for (j = 0; j < 8; j++, abits <<= 1, xbits <<= 1) { + for (j = 0; j < 8; j++, abits <<= 1, xbits <<= 1, idx++) { if (abits & BIT(7)) { if (xbits & BIT(7)) { - color = dp[i * 8 + j] ^ 0xffffffff; /* complement */ + color = dp[idx] ^ 0xffffffff; /* complement */ } else { continue; /* transparent, no change */ } @@ -224,10 +224,10 @@ static void ati_cursor_draw_line(VGACommonState *vga, uint8_t *d, int scr_y) color = (xbits & BIT(7) ? s->regs.cur_color1 : s->regs.cur_color0) | 0xff000000; } - if (vga->hw_cursor_x + i * 8 + j >= h) { + if (vga->hw_cursor_x + idx >= h) { return; /* end of screen, don't span to next line */ } - dp[i * 8 + j] = color; + dp[idx] = color; } } } From e9fa0330a20d117d70f604cb5a1fd647f531c1b7 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sun, 8 Mar 2026 23:49:17 +0100 Subject: [PATCH 46/49] ati-vga: Do not access pixel outside the screen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We check end of screen before writing the pixel but before that complement color also accesses screen pixel so we have to check before that. This fixes a segmentation fault with guest_hwcursor when pointer is partially out of screen at lower right corner. Signed-off-by: BALATON Zoltan Reviewed-by: Chad Jablonski Message-ID: <26db0715a6b9f6504f394010513facc9a37882ad.1773009887.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/ati.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/display/ati.c b/hw/display/ati.c index 2c649e940b..05cf507bd4 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -214,6 +214,9 @@ static void ati_cursor_draw_line(VGACommonState *vga, uint8_t *d, int scr_y) uint8_t abits = vga_read_byte(vga, srcoff + i); uint8_t xbits = vga_read_byte(vga, srcoff + i + 8); for (j = 0; j < 8; j++, abits <<= 1, xbits <<= 1, idx++) { + if (vga->hw_cursor_x + idx >= h) { + return; /* end of screen, don't span to next line */ + } if (abits & BIT(7)) { if (xbits & BIT(7)) { color = dp[idx] ^ 0xffffffff; /* complement */ @@ -224,9 +227,6 @@ static void ati_cursor_draw_line(VGACommonState *vga, uint8_t *d, int scr_y) color = (xbits & BIT(7) ? s->regs.cur_color1 : s->regs.cur_color0) | 0xff000000; } - if (vga->hw_cursor_x + idx >= h) { - return; /* end of screen, don't span to next line */ - } dp[idx] = color; } } From 8a16a38f3d1d4c023e27704fe76efd63ac175035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 9 Mar 2026 10:38:38 +0100 Subject: [PATCH 47/49] hw/i3c/dw-i3c: Fix memory leaks in error logging paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit object_get_canonical_path() returns an allocated string that must be freed by the caller. Use g_autofree variables to ensure that memory is not leaked. Resolves: Coverity CID 1645550 Resolves: Coverity CID 1645553 Signed-off-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260309093838.364126-1-clg@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i3c/dw-i3c.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/i3c/dw-i3c.c b/hw/i3c/dw-i3c.c index 3d8b95a14c..e9bdfd6af2 100644 --- a/hw/i3c/dw-i3c.c +++ b/hw/i3c/dw-i3c.c @@ -490,8 +490,9 @@ static int dw_i3c_recv_data(DWI3C *s, bool is_i2c, uint8_t *data, /* I3C devices can NACK if the controller sends an unsupported CCC. */ ret = i3c_recv(s->bus, data, num_to_read, num_read); if (ret) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); qemu_log_mask(LOG_GUEST_ERROR, "%s: NACKed receiving byte\n", - object_get_canonical_path(OBJECT(s))); + path); ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_ST_STATUS, DW_I3C_TRANSFER_STATE_HALT); ARRAY_FIELD_DP32(s->regs, PRESENT_STATE, CM_TFR_STATUS, @@ -1107,8 +1108,9 @@ static void dw_i3c_resp_queue_push(DWI3C *s, uint8_t err, uint8_t tid, static void dw_i3c_push_tx(DWI3C *s, uint32_t val) { if (fifo32_is_full(&s->tx_queue)) { + g_autofree char *path = object_get_canonical_path(OBJECT(s)); qemu_log_mask(LOG_GUEST_ERROR, "%s: Tried to push to TX FIFO when " - "full\n", object_get_canonical_path(OBJECT(s))); + "full\n", path); return; } From ac9dde499effc2e25449cb4b87aca3f9f9cfbd0a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 9 Mar 2026 09:51:29 +0000 Subject: [PATCH 48/49] hw/misc/sifive_e_aon: Don't leak timer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sifive_e_aon watchdog creates a timer with timer_new_ns() in its instance_init method, but does not free it in instance_finalize. This means that QMP introspection of the device leaks it: Direct leak of 48 byte in 1 object allocated from: #0 in calloc #1 in g_malloc0 #2 in timer_new_full /home/pm215/qemu/include/qemu/timer.h:520:21 #3 in timer_new /home/pm215/qemu/include/qemu/timer.h:543:12 #4 in timer_new_ns /home/pm215/qemu/include/qemu/timer.h:563:12 #5 in sifive_e_aon_init /home/pm215/qemu/build/san/../../hw/misc/sifive_e_aon.c:286:21 #6 in object_initialize_with_type /home/pm215/qemu/build/san/../../qom/object.c:570:5 #7 in object_initialize /home/pm215/qemu/build/san/../../qom/object.c:578:5 #8 in object_initialize_child_with_propsv /home/pm215/qemu/build/san/../../qom/object.c:608:5 #9 in object_initialize_child_with_props /home/pm215/qemu/build/san/../../qom/object.c:591:10 #10 in object_initialize_child_internal /home/pm215/qemu/build/san/../../qom/object.c:645:5 #11 in object_initialize_with_type /home/pm215/qemu/build/san/../../qom/object.c:570:5 #12 in object_new_with_type /home/pm215/qemu/build/san/../../qom/object.c:774:5 #13 in qmp_device_list_properties /home/pm215/qemu/build/san/../../qom/qom-qmp-cmds.c:206:11 Allocating a separate QEMUTimer with timer_new() is not the preferred interface (per the comments in include/qemu/timer.h); switch to an inline struct initialized with timer_init(), which we can clean up with timer_del() in finalize. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260309095129.1406506-1-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/misc/sifive_e_aon.c | 16 ++++++++++++---- include/hw/misc/sifive_e_aon.h | 2 +- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/hw/misc/sifive_e_aon.c b/hw/misc/sifive_e_aon.c index 0e82ae3758..e78f4f5672 100644 --- a/hw/misc/sifive_e_aon.c +++ b/hw/misc/sifive_e_aon.c @@ -94,9 +94,9 @@ static void sifive_e_aon_wdt_update_state(SiFiveEAONState *r) next += muldiv64((r->wdogcmp0 - wdogs) << FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, SCALE), NANOSECONDS_PER_SECOND, r->wdogclk_freq); - timer_mod(r->wdog_timer, next); + timer_mod(&r->wdog_timer, next); } else { - timer_mod(r->wdog_timer, INT64_MAX); + timer_mod(&r->wdog_timer, INT64_MAX); } } @@ -283,12 +283,19 @@ static void sifive_e_aon_init(Object *obj) sysbus_init_mmio(sbd, &r->mmio); /* watchdog timer */ - r->wdog_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - sifive_e_aon_wdt_expired_cb, r); + timer_init_ns(&r->wdog_timer, QEMU_CLOCK_VIRTUAL, + sifive_e_aon_wdt_expired_cb, r); r->wdogclk_freq = SIFIVE_E_LFCLK_DEFAULT_FREQ; sysbus_init_irq(sbd, &r->wdog_irq); } +static void sifive_e_aon_finalize(Object *obj) +{ + SiFiveEAONState *r = SIFIVE_E_AON(obj); + + timer_del(&r->wdog_timer); +} + static const Property sifive_e_aon_properties[] = { DEFINE_PROP_UINT64("wdogclk-frequency", SiFiveEAONState, wdogclk_freq, SIFIVE_E_LFCLK_DEFAULT_FREQ), @@ -307,6 +314,7 @@ static const TypeInfo sifive_e_aon_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SiFiveEAONState), .instance_init = sifive_e_aon_init, + .instance_finalize = sifive_e_aon_finalize, .class_init = sifive_e_aon_class_init, }; diff --git a/include/hw/misc/sifive_e_aon.h b/include/hw/misc/sifive_e_aon.h index efa2c3023f..e907aa7869 100644 --- a/include/hw/misc/sifive_e_aon.h +++ b/include/hw/misc/sifive_e_aon.h @@ -46,7 +46,7 @@ struct SiFiveEAONState { MemoryRegion mmio; /*< watchdog timer >*/ - QEMUTimer *wdog_timer; + QEMUTimer wdog_timer; qemu_irq wdog_irq; uint64_t wdog_restart_time; uint64_t wdogclk_freq; From caa5d8257037201f4e546ec833e51863ae07b331 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 9 Mar 2026 09:53:28 +0000 Subject: [PATCH 49/49] hw/misc/riscv_cpc: Free CPU array in instance_finalize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TYPE_RISCV_CPC device allocates an array in its instance_init, but does not free this, leading to leaks like this from QOM/QMP introspection: Direct leak of 512 byte in 1 object allocated from: #0 in calloc #1 in g_malloc0 #2 in riscv_cpc_init /home/pm215/qemu/build/san/../../hw/misc/riscv_cpc.c:175:15 #3 in object_initialize_with_type /home/pm215/qemu/build/san/../../qom/object.c:570:5 #4 in object_new_with_type /home/pm215/qemu/build/san/../../qom/object.c:774:5 #5 in qmp_device_list_properties /home/pm215/qemu/build/san/../../qom/qom-qmp-cmds.c:206:11 #6 in qdev_device_help /home/pm215/qemu/build/san/../../system/qdev-monitor.c:313:17 #7 in hmp_device_add /home/pm215/qemu/build/san/../../system/qdev-monitor.c:1005:9 Free the array in instance_finalize. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260309095328.1406779-1-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/misc/riscv_cpc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hw/misc/riscv_cpc.c b/hw/misc/riscv_cpc.c index 231a419062..4bf2fd8db1 100644 --- a/hw/misc/riscv_cpc.c +++ b/hw/misc/riscv_cpc.c @@ -185,6 +185,13 @@ static void riscv_cpc_init(Object *obj) } } +static void riscv_cpc_finalize(Object *obj) +{ + RISCVCPCState *s = RISCV_CPC(obj); + + g_free(s->cpus); +} + static void riscv_cpc_realize(DeviceState *dev, Error **errp) { RISCVCPCState *s = RISCV_CPC(dev); @@ -254,6 +261,7 @@ static const TypeInfo riscv_cpc_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(RISCVCPCState), .instance_init = riscv_cpc_init, + .instance_finalize = riscv_cpc_finalize, .class_init = riscv_cpc_class_init, };